Subversion-Projekte lars-tiefland.ci

Revision

Revision 2242 | Zur aktuellen Revision | Blame | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @package     CodeIgniter
 * @author      EllisLab Dev Team
 * @copyright   Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
 * @copyright   Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
 * @license     http://opensource.org/licenses/MIT      MIT License
 * @link        https://codeigniter.com
 * @since       Version 1.0.0
 * @filesource
 */
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Router Class
 *
 * Parses URIs and determines routing
 *
 * @package             CodeIgniter
 * @subpackage  Libraries
 * @category    Libraries
 * @author              EllisLab Dev Team
 * @link                https://codeigniter.com/user_guide/general/routing.html
 */
class CI_Router {

        /**
         * CI_Config class object
         *
         * @var object
         */
        public $config;

        /**
         * List of routes
         *
         * @var array
         */
        public $routes =        array();

        /**
         * Current class name
         *
         * @var string
         */
        public $class =         '';

        /**
         * Current method name
         *
         * @var string
         */
        public $method =        'index';

        /**
         * Sub-directory that contains the requested controller class
         *
         * @var string
         */
        public $directory;

        /**
         * Default controller (and method if specific)
         *
         * @var string
         */
        public $default_controller;

        /**
         * Translate URI dashes
         *
         * Determines whether dashes in controller & method segments
         * should be automatically replaced by underscores.
         *
         * @var bool
         */
        public $translate_uri_dashes = FALSE;

        /**
         * Enable query strings flag
         *
         * Determines whether to use GET parameters or segment URIs
         *
         * @var bool
         */
        public $enable_query_strings = FALSE;

        // --------------------------------------------------------------------

        /**
         * Class constructor
         *
         * Runs the route mapping function.
         *
         * @param       array   $routing
         * @return      void
         */
        public function __construct($routing = NULL)
        {
                $this->config =& load_class('Config', 'core');
                $this->uri =& load_class('URI', 'core');

                $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);

                // If a directory override is configured, it has to be set before any dynamic routing logic
                is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);
                $this->_set_routing();

                // Set any routing overrides that may exist in the main index file
                if (is_array($routing))
                {
                        empty($routing['controller']) OR $this->set_class($routing['controller']);
                        empty($routing['function'])   OR $this->set_method($routing['function']);
                }

                log_message('info', 'Router Class Initialized');
        }

        // --------------------------------------------------------------------

        /**
         * Set route mapping
         *
         * Determines what should be served based on the URI request,
         * as well as any "routes" that have been set in the routing config file.
         *
         * @return      void
         */
        protected function _set_routing()
        {
                // Load the routes.php file. It would be great if we could
                // skip this for enable_query_strings = TRUE, but then
                // default_controller would be empty ...
                if (file_exists(APPPATH.'config/routes.php'))
                {
                        include(APPPATH.'config/routes.php');
                }

                if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
                {
                        include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
                }

                // Validate & get reserved routes
                if (isset($route) && is_array($route))
                {
                        isset($route['default_controller']) && $this->default_controller = $route['default_controller'];
                        isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes'];
                        unset($route['default_controller'], $route['translate_uri_dashes']);
                        $this->routes = $route;
                }

                // Are query strings enabled in the config file? Normally CI doesn't utilize query strings
                // since URI segments are more search-engine friendly, but they can optionally be used.
                // If this feature is enabled, we will gather the directory/class/method a little differently
                if ($this->enable_query_strings)
                {
                        // If the directory is set at this time, it means an override exists, so skip the checks
                        if ( ! isset($this->directory))
                        {
                                $_d = $this->config->item('directory_trigger');
                                $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : '';

                                if ($_d !== '')
                                {
                                        $this->uri->filter_uri($_d);
                                        $this->set_directory($_d);
                                }
                        }

                        $_c = trim($this->config->item('controller_trigger'));
                        if ( ! empty($_GET[$_c]))
                        {
                                $this->uri->filter_uri($_GET[$_c]);
                                $this->set_class($_GET[$_c]);

                                $_f = trim($this->config->item('function_trigger'));
                                if ( ! empty($_GET[$_f]))
                                {
                                        $this->uri->filter_uri($_GET[$_f]);
                                        $this->set_method($_GET[$_f]);
                                }

                                $this->uri->rsegments = array(
                                        1 => $this->class,
                                        2 => $this->method
                                );
                        }
                        else
                        {
                                $this->_set_default_controller();
                        }

                        // Routing rules don't apply to query strings and we don't need to detect
                        // directories, so we're done here
                        return;
                }

                // Is there anything to parse?
                if ($this->uri->uri_string !== '')
                {
                        $this->_parse_routes();
                }
                else
                {
                        $this->_set_default_controller();
                }
        }

        // --------------------------------------------------------------------

        /**
         * Set request route
         *
         * Takes an array of URI segments as input and sets the class/method
         * to be called.
         *
         * @used-by     CI_Router::_parse_routes()
         * @param       array   $segments       URI segments
         * @return      void
         */
        protected function _set_request($segments = array())
        {
                $segments = $this->_validate_request($segments);
                // If we don't have any segments left - try the default controller;
                // WARNING: Directories get shifted out of the segments array!
                if (empty($segments))
                {
                        $this->_set_default_controller();
                        return;
                }

                if ($this->translate_uri_dashes === TRUE)
                {
                        $segments[0] = str_replace('-', '_', $segments[0]);
                        if (isset($segments[1]))
                        {
                                $segments[1] = str_replace('-', '_', $segments[1]);
                        }
                }

                $this->set_class($segments[0]);
                if (isset($segments[1]))
                {
                        $this->set_method($segments[1]);
                }
                else
                {
                        $segments[1] = 'index';
                }

                array_unshift($segments, NULL);
                unset($segments[0]);
                $this->uri->rsegments = $segments;
        }

        // --------------------------------------------------------------------

        /**
         * Set default controller
         *
         * @return      void
         */
        protected function _set_default_controller()
        {
                if (empty($this->default_controller))
                {
                        show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
                }

                // Is the method being specified?
                if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2)
                {
                        $method = 'index';
                }

                if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php'))
                {
                        // This will trigger 404 later
                        return;
                }

                $this->set_class($class);
                $this->set_method($method);

                // Assign routed segments, index starting from 1
                $this->uri->rsegments = array(
                        1 => $class,
                        2 => $method
                );

                log_message('debug', 'No URI present. Default controller set.');
        }

        // --------------------------------------------------------------------

        /**
         * Validate request
         *
         * Attempts validate the URI request and determine the controller path.
         *
         * @used-by     CI_Router::_set_request()
         * @param       array   $segments       URI segments
         * @return      mixed   URI segments
         */
        protected function _validate_request($segments)
        {
                $c = count($segments);
                $directory_override = isset($this->directory);

                // Loop through our segments and return as soon as a controller
                // is found or when such a directory doesn't exist
                while ($c-- > 0)
                {
                        $test = $this->directory
                                .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]);

                        if ( ! file_exists(APPPATH.'controllers/'.$test.'.php')
                                && $directory_override === FALSE
                                && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0])
                        )
                        {
                                $this->set_directory(array_shift($segments), TRUE);
                                continue;
                        }

                        return $segments;
                }

                // This means that all segments were actually directories
                return $segments;
        }

        // --------------------------------------------------------------------

        /**
         * Parse Routes
         *
         * Matches any routes that may exist in the config/routes.php file
         * against the URI to determine if the class/method need to be remapped.
         *
         * @return      void
         */
        protected function _parse_routes()
        {
                // Turn the segment array into a URI string
                $uri = implode('/', $this->uri->segments);

                // Get HTTP verb
                $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';

                // Loop through the route array looking for wildcards
                foreach ($this->routes as $key => $val)
                {
                        // Check if route format is using HTTP verbs
                        if (is_array($val))
                        {
                                $val = array_change_key_case($val, CASE_LOWER);
                                if (isset($val[$http_verb]))
                                {
                                        $val = $val[$http_verb];
                                }
                                else
                                {
                                        continue;
                                }
                        }

                        // Convert wildcards to RegEx
                        $key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);

                        // Does the RegEx match?
                        if (preg_match('#^'.$key.'$#', $uri, $matches))
                        {
                                // Are we using callbacks to process back-references?
                                if ( ! is_string($val) && is_callable($val))
                                {
                                        // Remove the original string from the matches array.
                                        array_shift($matches);

                                        // Execute the callback using the values in matches as its parameters.
                                        $val = call_user_func_array($val, $matches);
                                }
                                // Are we using the default routing method for back-references?
                                elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE)
                                {
                                        $val = preg_replace('#^'.$key.'$#', $val, $uri);
                                }

                                $this->_set_request(explode('/', $val));
                                return;
                        }
                }

                // If we got this far it means we didn't encounter a
                // matching route so we'll set the site default route
                $this->_set_request(array_values($this->uri->segments));
        }

        // --------------------------------------------------------------------

        /**
         * Set class name
         *
         * @param       string  $class  Class name
         * @return      void
         */
        public function set_class($class)
        {
                $this->class = str_replace(array('/', '.'), '', $class);
        }

        // --------------------------------------------------------------------

        /**
         * Fetch the current class
         *
         * @deprecated  3.0.0   Read the 'class' property instead
         * @return      string
         */
        public function fetch_class()
        {
                return $this->class;
        }

        // --------------------------------------------------------------------

        /**
         * Set method name
         *
         * @param       string  $method Method name
         * @return      void
         */
        public function set_method($method)
        {
                $this->method = $method;
        }

        // --------------------------------------------------------------------

        /**
         * Fetch the current method
         *
         * @deprecated  3.0.0   Read the 'method' property instead
         * @return      string
         */
        public function fetch_method()
        {
                return $this->method;
        }

        // --------------------------------------------------------------------

        /**
         * Set directory name
         *
         * @param       string  $dir    Directory name
         * @param       bool    $append Whether we're appending rather than setting the full value
         * @return      void
         */
        public function set_directory($dir, $append = FALSE)
        {
                if ($append !== TRUE OR empty($this->directory))
                {
                        $this->directory = str_replace('.', '', trim($dir, '/')).'/';
                }
                else
                {
                        $this->directory .= str_replace('.', '', trim($dir, '/')).'/';
                }
        }

        // --------------------------------------------------------------------

        /**
         * Fetch directory
         *
         * Feches the sub-directory (if any) that contains the requested
         * controller class.
         *
         * @deprecated  3.0.0   Read the 'directory' property instead
         * @return      string
         */
        public function fetch_directory()
        {
                return $this->directory;
        }

}