zoukankan      html  css  js  c++  java
  • Drupal启动阶段之一:配置

    配置是Drupal启动过程中的第一个阶段,通过函数_drupal_bootstrap_configuration()实现:

    function _drupal_bootstrap_configuration() {
      set_error_handler('_drupal_error_handler');
      set_exception_handler('_drupal_exception_handler');
    
      drupal_environment_initialize();
      timer_start('page');
      drupal_settings_initialize();
    }

    错误和异常处理

    Drupal实现了自己的错误处理器_drupal_error_handler()和异常处理器_drupal_exception_handler(),抽时间可以研究下具体如何实现的。

    set_error_handler('_drupal_error_handler');
    set_exception_handler('_drupal_exception_handler');

    初始化环境

    初始化环境通过函数drupal_environment_initialize()实现。首先,对$_SERVER中的一些变量检查和修改,保证后续需要用到时都是处理过的:

    if (!isset($_SERVER['HTTP_REFERER'])) {
      $_SERVER['HTTP_REFERER'] = '';
    }
    if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) {
      $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
    }
    
    if (isset($_SERVER['HTTP_HOST'])) {
      // As HTTP_HOST is user input, ensure it only contains characters allowed
      // in hostnames. See RFC 952 (and RFC 2181).
      // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
      $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
      if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) {
        // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
        header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
        exit;
      }
    }
    else {
      // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
      // defined for E_ALL compliance.
      $_SERVER['HTTP_HOST'] = '';
    }

    再次,检查用户请求的路径,放到$_GET['q']里面:

    $_GET['q'] = request_path();

    函数request_path()需要判断请求URL中是否有包含?q=XXX这样的查询参数。有时候为了让搜索引擎友好,会重写URL,也会出现http://example.com/node/306这样的情况。

    function request_path() {
      static $path;
    
      if (isset($path)) {
        return $path;
      }
    
      if (isset($_GET['q']) && is_string($_GET['q'])) {
        // This is a request with a ?q=foo/bar query string. $_GET['q'] is
        // overwritten in drupal_path_initialize(), but request_path() is called
        // very early in the bootstrap process, so the original value is saved in
        // $path and returned in later calls.
        $path = $_GET['q'];
      }
      elseif (isset($_SERVER['REQUEST_URI'])) {
        // This request is either a clean URL, or 'index.php', or nonsense.
        // Extract the path from REQUEST_URI.
        // $_SERVER['REQUEST_URI'] = /drupal/index.php?debug_host=10.0.2.15&debug_port=10137&debug_session_id=1000这样
        // 进过strtok()去掉问号的内容后 $request_path = /drupal/index.php
        $request_path = strtok($_SERVER['REQUEST_URI'], '?');
        
        // $_SERVER['SCRIPT_NAME'] = /drupal/index.php
        // $base_path_len = strlen(/drupal)
        $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '/'));
        
        // Unescape and strip $base_path prefix, leaving q without a leading slash.
        // $path = index.php
        $path = substr(urldecode($request_path), $base_path_len + 1);
        
        // If the path equals the script filename, either because 'index.php' was
        // explicitly provided in the URL, or because the server added it to
        // $_SERVER['REQUEST_URI'] even when it wasn't provided in the URL (some
        // versions of Microsoft IIS do this), the front page should be served.
        // $_SERVER['PHP_SELF'] = /drupal/index.php
        if ($path == basename($_SERVER['PHP_SELF'])) {
          $path = '';
        }
        
        // $_SERVER['REQUEST_URI'] = /drupal/node/306
        //    $request_path = /drupal/node/306
        //    $base_path_len = 7, 不变
        //    $path = node/306
      }
      else {
        // This is the front page.
        $path = '';
      }
    
      // Under certain conditions Apache's RewriteRule directive prepends the value
      // assigned to $_GET['q'] with a slash. Moreover we can always have a trailing
      // slash in place, hence we need to normalize $_GET['q'].
      $path = trim($path, '/');
    
      return $path;
    }

    然后,修改了一些PHP设置。需要注意一下Drupal使用了cookie来传递会话ID,避免了将会话ID放在查询参数里面,导致请求URI太长:

    // Don't escape quotes when reading files from the database, disk, etc.
    ini_set('magic_quotes_runtime', '0');
    // Use session cookies, not transparent sessions that puts the session id in
    // the query string.
    ini_set('session.use_cookies', '1');
    ini_set('session.use_only_cookies', '1');
    ini_set('session.use_trans_sid', '0');
    // Don't send HTTP headers using PHP's session handler.
    ini_set('session.cache_limiter', 'none');
    // Use httponly session cookies.
    ini_set('session.cookie_httponly', '1');

    初始化设置

    初始化设置是通过函数drupal_settings_initialize()完成的。这里的设置是指Drupal的settings.php文件。需要注意的是,这里许多变量都做了global声明,也就是说这些变量在Drupal都是全局的。

    global $base_url, $base_path, $base_root;
    
    // Export these settings.php variables to the global namespace.
    global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;


    首先,直接导入settings.php文件:

    if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
        include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
    }

    conf_path()用来处理多站点的情形,具体可以参看《Drupal配置文件settings.php搜索规则》。
    settings.php文件必须申明数据库连接信息$databases。另外的,也可以设置Drupal配置变量。在settings.php文件中设置的配置变量具有最高优先级,会覆盖掉后台数据库variable表中的变量,这些变量可以通过variable_get()和variable_set()访问,可以参考《Drupal如何处理系统变量》。例如,可以在settings.php设置代理信息:

    $conf['proxy_server'] = '';
    $conf['proxy_port'] = 8080;
    $conf['proxy_username'] = '';
    $conf['proxy_password'] = '';
    $conf['proxy_user_agent'] = '';
    $conf['proxy_exceptions'] = array('127.0.0.1', 'localhost');


    然后,处理各种与URL有关的信息:

    $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
    
    if (isset($base_url)) {
      // Parse fixed base URL from settings.php.
      $parts = parse_url($base_url);
      if (!isset($parts['path'])) {
        $parts['path'] = '';
      }
      $base_path = $parts['path'] . '/';
      // Build $base_root (everything until first slash after "scheme://").
      $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
    }
    else {
      // Create base URL.
      $http_protocol = $is_https ? 'https' : 'http';
      
      // http://localhost
      $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
    
      $base_url = $base_root;
    
      // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
      // be modified by a visitor.
      if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/')) {
        $base_path = $dir;
        $base_url .= $base_path;
        $base_path .= '/';
      }
      else {
        $base_path = '/';
      }
      
      // $base_path = /drupal/
      // $base_url = http://localhost/drupal/
    }
    
    $base_secure_url = str_replace('http://', 'https://', $base_url);
    $base_insecure_url = str_replace('https://', 'http://', $base_url);


    最后,处理Cookie和会话有关的信息:

    if ($cookie_domain) {
      // If the user specifies the cookie domain, also use it for session name.
      $session_name = $cookie_domain;
    }
    else {
      // Otherwise use $base_url as session name, without the protocol
      // to use the same session identifiers across HTTP and HTTPS.
      list( , $session_name) = explode('://', $base_url, 2);
      // 最后的会话Cookie名称是 session_name('SESS' . substr(hash('sha256', 'localhost/drupal'), 0, 32))
      
      // HTTP_HOST can be modified by a visitor, but we already sanitized it
      // in drupal_settings_initialize().
      if (!empty($_SERVER['HTTP_HOST'])) {
        $cookie_domain = $_SERVER['HTTP_HOST'];
        // Strip leading periods, www., and port numbers from cookie domain.
        $cookie_domain = ltrim($cookie_domain, '.');
        if (strpos($cookie_domain, 'www.') === 0) {
          $cookie_domain = substr($cookie_domain, 4);
        }
        $cookie_domain = explode(':', $cookie_domain);
        $cookie_domain = '.' . $cookie_domain[0];
      }
    }
    // Per RFC 2109, cookie domains must contain at least one dot other than the
    // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
    if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
      ini_set('session.cookie_domain', $cookie_domain);
    }
    // To prevent session cookies from being hijacked, a user can configure the
    // SSL version of their website to only transfer session cookies via SSL by
    // using PHP's session.cookie_secure setting. The browser will then use two
    // separate session cookies for the HTTPS and HTTP versions of the site. So we
    // must use different session identifiers for HTTPS and HTTP to prevent a
    // cookie collision.
    if ($is_https) {
      ini_set('session.cookie_secure', TRUE);
    }
    $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS';
    session_name($prefix . substr(hash('sha256', $session_name), 0, 32));

    这里说明一下,Drupal的会话Cookie名称使用了SESS前缀再加上URL路径的形式:

    session_name('SESS' . substr(hash('sha256', 'localhost/drupal'), 0, 32))

    透过Chrome浏览器可以很直观地看到这一点:

  • 相关阅读:
    C语言-if语句
    C语言-表达式
    C语言-基础
    Java for LeetCode 187 Repeated DNA Sequences
    Java for LeetCode 179 Largest Number
    Java for LeetCode 174 Dungeon Game
    Java for LeetCode 173 Binary Search Tree Iterator
    Java for LeetCode 172 Factorial Trailing Zeroes
    Java for LeetCode 171 Excel Sheet Column Number
    Java for LeetCode 169 Majority Element
  • 原文地址:https://www.cnblogs.com/eastson/p/3356611.html
Copyright © 2011-2022 走看看