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浏览器可以很直观地看到这一点:

  • 相关阅读:
    泛海精灵软件预发布统计报告 & 反馈
    【scrum】2.23
    VS2010中C#添加图片(资源)
    用XML存储程序的配置
    scrum 2.28
    【Scrum】2.24
    《人月神话》读书心得
    微软R&D喜欢什么人才
    “电脑族”应多做下肢运动
    ASCII表
  • 原文地址:https://www.cnblogs.com/eastson/p/3356611.html
Copyright © 2011-2022 走看看