2010-06-16 27 views
61

Tôi đang cố gắng đặt biến môi trường Apache (để sử dụng trong PHP) với cờ [E=VAR:VAL] trên quy tắc RewriteRule trong tệp .htaccess.Khi đặt biến môi trường trong chỉ thị Apache RewriteRule, điều gì khiến tên biến được đặt trước bằng "REDIRECT_"?

Tôi đã phát hiện ra các biến được truy cập trong PHP dưới dạng biến máy chủ $_SERVER thay vì $_ENV (có ý nghĩa nhất định). Tuy nhiên, vấn đề của tôi là dành cho một số quy tắc cờ [E=VAR:VAL] việc như mong đợi và tôi kết thúc với một biến $_SERVER['VAR'] nhưng đối với quy tắc khác tôi kết thúc với một biến $_SERVER['REDIRECT_VAR'] hoặc $_SERVER['REDIRECT_REDIRECT_VAR'], vv

A. Điều gì gây ra một biến môi trường thiết lập trong Apache sử dụng cờ [E=VAR:VAL] để được đổi tên bằng cách đặt "REDIRECT_" được thêm vào tên biến?

B. Tôi có thể làm gì để đảm bảo tôi kết thúc với Biến môi trường có tên không thay đổi để tôi có thể truy cập vào PHP dưới dạng $_SERVER['VAR'] mà không phải sử dụng để kiểm tra các biến thể của tên biến có một trong nhiều phiên bản của "REDIRECT_" được thêm vào nó?

Giải pháp một phần được tìm thấy. Thêm dòng sau vào khi bắt đầu viết lại quy tắc tái tạo ENV ban đầu: VAR trên mỗi chuyển hướng (cũng như rời khỏi phiên bản REDIRECT_VAR đó) nếu họ đang cần:

RewriteCond %{ENV:REDIRECT_VAR} !^$ 
RewriteRule .* - [E=VAR:%{ENV:REDIRECT_VAR}] 
+0

Tôi luôn sử dụng getenv() - http://php.net/manual/en/function.getenv.php và chưa gặp phải bất kỳ sự cố lạ nào. – Chaoley

Trả lời

8

tôi đã không kiểm tra này ở tất cả các và tôi biết nó không đề cập đến điểm A hoặc B, nhưng có một số mô tả về vấn đề này trong các ý kiến ​​trong tài liệu PHP và một số giải pháp khả thi cho việc truy cập các biến sử dụng $_SERVER['VAR']:

http://www.php.net/manual/en/reserved.variables.php#79811

EDIT - một số câu trả lời khác cho câu hỏi được cung cấp:

A: Các biến môi trường được đổi tên bởi Apache nếu chúng có liên quan đến chuyển hướng. Ví dụ, nếu bạn có các nguyên tắc sau:

RewriteRule ^index.php - [E=VAR1:'hello',E=VAR2:'world'] 

Sau đó, bạn có thể truy cập VAR1 và var2 sử dụng $_SERVER['VAR1']$_SERVER['VAR2']. Tuy nhiên, nếu bạn chuyển hướng trang như vậy:

RewriteRule ^index.php index2.php [E=VAR1:'hello',E=VAR2:'world'] 

Sau đó, bạn phải sử dụng $_SERVER['REDIRECT_VAR1'] vv

B: Cách tốt nhất để khắc phục vấn đề này được xử lý các biến mà bạn quan tâm bằng cách sử dụng PHP. Tạo một hàm chạy qua mảng $_SERVER và tìm các mục bạn cần. Thậm chí bạn có thể sử dụng một chức năng như sau:

function myGetEnv($key) { 
    $prefix = "REDIRECT_"; 
    if(array_key_exists($key, $_SERVER)) 
     return $_SERVER[$key]; 
    foreach($_SERVER as $k=>$v) { 
     if(substr($k, 0, strlen($prefix)) == $prefix) { 
      if(substr($k, -(strlen($key))) == $key) 
       return $v; 
     } 
    } 
    return null; 
} 
+0

Cảm ơn bạn đã gửi liên kết tới nhận xét. Họ xác nhận vấn đề và cung cấp cách giải quyết nhưng nó chỉ thay đổi vấn đề từ PHP sang mã phụ trong .htaccess Tôi hy vọng một guru Apache có thể biết làm thế nào để làm cho tên tồn tại nên không có mã workaround là cần thiết! (Tôi sẽ cung cấp cho bạn +1 nhưng không có đủ đại diện để làm điều đó) – trowel

+0

@trowel - xem các thay đổi cho câu trả lời của tôi – thetaiko

+0

Cảm ơn thetaiko, nó transpires đổi tên là một tính năng được giới thiệu trong Apache 2.0, được thiết kế để giúp theo dõi biến thông qua chuyển hướng. Chức năng PHP đẹp nhưng tôi có thể đi với một preg_match bắt đầu bằng/^ ($ REDIRECT _) * để nắm bắt bất kỳ số lượng chuyển hướng nào trong một lần truy cập. Vào cuối apache của những điều tôi đã tìm thấy một giải pháp (thêm vào câu hỏi) – trowel

68

Hành vi này không may và thậm chí không xuất hiện trong tài liệu.

.htaccess mỗi dir bối cảnh

Đây là những gì dường như xảy ra trong .htaccess mỗi thư mục (mỗi thư mục) ngữ cảnh:

Giả sử rằng Apache xử lý một tập tin .htaccess bao gồm viết lại chỉ thị.

  1. Apache populates môi trường đồ biến của nó với tất cả các CGI tiêu chuẩn/Apache biến

  2. Viết lại bắt đầu

  3. biến môi trường được thiết lập trong RewriteRule chỉ

  4. Khi Apache dừng xử lý RewriteRule chỉ thị (vì một L cờ hoặc cuối ruleset) và URL đã được được thay đổi bởi một RewriteRule, Apache khởi động lại yêu cầu xử lý.

    Nếu bạn không quen thuộc với phần này, xem L flag documentation:

    do đó ruleset chưa chạy lại từ đầu. Thông thường nhất điều này sẽ xảy ra nếu một trong các quy tắc gây ra chuyển hướng - bên trong hoặc bên ngoài - khiến quá trình yêu cầu bắt đầu lại.
  5. Từ những gì tôi có thể quan sát, tôi tin rằng khi # 4 xảy ra, # 1 được lặp đi lặp lại, sau đó các biến môi trường đã được thiết lập trong RewriteRule chỉ được thêm vào phía trước với REDIRECT_ và bổ sung vào môi trường bản đồ vars (không nhất thiết theo thứ tự đó, nhưng kết quả cuối cùng bao gồm kết hợp đó).

    Bước này là nơi các tên biến được chọn bị xóa sổ, và trong giây lát tôi sẽ giải thích tại sao điều đó lại quan trọng và bất tiện.

Khôi phục tên biến

Khi tôi ban đầu chạy vào vấn đề này, tôi đang làm một cái gì đó như sau trong .htaccess (giản thể):

RewriteCond %{HTTP_HOST} (.+)\.projects\. 

RewriteRule (.*) subdomains/%1/docroot/$1 

RewriteRule (.+/docroot)/ - [L,E=EFFECTIVE_DOCUMENT_ROOT:$1] 

Nếu tôi được đặt biến môi trường trong RewriteRule đầu tiên, Apache sẽ khởi động lại quá trình viết lại và thêm biến vào trước với REDIRECT_ (các bướC# 4 & 5 ở trên), do đó tôi sẽ mất quyền truy cập vào nó thông qua tên mà tôi đã chỉ định.

Trong trường hợp này, RewriteRule thay đổi URL đầu tiên, vì vậy sau khi cả hai RewriteRule được xử lý, Apache khởi động lại quy trình và xử lý lại .htaccess. Lần thứ hai, RewriteRule đầu tiên bị bỏ qua vì chỉ thị RewriteCond, nhưng đối sánh thứ hai RewriteRule, đặt biến môi trường (lại) và quan trọng là không thay đổi URL. Vì vậy, yêu cầu/viết lại quá trình không bắt đầu lại, và tên biến tôi đã chọn gậy. Trong trường hợp này, tôi thực sự có cả REDIRECT_EFFECTIVE_DOCUMENT_ROOTEFFECTIVE_DOCUMENT_ROOT. Nếu tôi sử dụng cờ L trên RewriteRule đầu tiên, tôi chỉ có EFFECTIVE_DOCUMENT_ROOT.

giải pháp một phần của trowel hoạt động tương tự: chỉ thị viết lại được xử lý lại, biến được đổi tên được gán lại tên gốc và nếu URL không thay đổi, quá trình kết thúc và tên biến được gán.

Tại sao những kỹ thuật là không đủ

Cả hai của những kỹ thuật bị một lỗ hổng lớn: khi viết lại quy tắc trong file .htaccess nơi bạn thiết lập các biến môi trường viết lại URL vào một thư mục lồng nhau sâu sắc hơn rằng có một tập tin .htaccess mà không có bất kỳ viết lại, tên biến được chỉ định của bạn sẽ bị xóa một lần nữa.

Giả sử bạn có một bố trí thư mục như thế này:

docroot/ 
     .htaccess 
     A.php 
     B.php 
     sub/ 
       .htaccess 
       A.php 
       B.php 

Và một docroot/.htaccess như thế này:

RewriteRule ^A\.php sub/B.php [L] 

RewriteRule .* - [E=MAJOR:flaw] 

Vì vậy, bạn yêu cầu /A.php, và nó được viết lại để sub/B.php. Bạn vẫn có biến số MAJOR.

Tuy nhiên, nếu bạn có bất kỳ chỉ thị viết lại nào trong docroot/sub/.htaccess (thậm chí chỉ RewriteEngine Off hoặc RewriteEngine On), biến số MAJOR của bạn biến mất. Đó là bởi vì khi URL được viết lại thành sub/B.php, docroot/sub/.htaccess được xử lý và nếu nó chứa bất kỳ chỉ thị viết lại nào, thì chỉ thị viết lại trong docroot/.htaccess sẽ không được xử lý lại. Nếu bạn đã có REDIRECT_MAJOR sau khi docroot/.htaccess được xử lý (ví dụ: nếu bạn bỏ qua cờ L từ RewriteRule đầu tiên), bạn vẫn sẽ có nó, nhưng những chỉ thị đó sẽ không chạy lại để đặt tên biến bạn đã chọn.

Inheritance

Vì vậy, nói rằng bạn muốn:

  1. biến môi trường thiết lập trong RewriteRule chỉ ở một mức độ cụ thể của cây thư mục (như docroot/.htaccess)

  2. có họ sẵn trong kịch bản ở mức sâu hơn

  3. có họ sẵn với tên giao

  4. có thể có chỉ thị viết lại nhiều hơn lồng sâu .htaccess file

Một giải pháp khả thi là sử dụng RewriteOptions inherit chỉ thị trong lồng nhau sâu sắc hơn .htaccess tập tin. Điều đó cho phép bạn chạy lại các chỉ thị viết lại trong các tệp lồng nhau ít sâu hơn và sử dụng các kỹ thuật được nêu ở trên để đặt các biến với các tên được chọn. Tuy nhiên, lưu ý rằng điều này làm tăng sự phức tạp bởi vì bạn phải cẩn thận hơn khi viết lại các chỉ thị trong các tệp lồng nhau ít sâu hơn để chúng không gây ra vấn đề khi chạy lại từ các thư mục lồng nhau sâu hơn. Tôi tin rằng Apache dải tiền tố cho mỗi thư mục cho các thư mục lồng nhau sâu hơn và chạy các chỉ thị viết lại trong các tập tin lồng nhau ít sâu hơn về giá trị đó.

@ kỹ thuật bay của

Theo như tôi thấy, hỗ trợ cho việc sử dụng một cấu trúc giống như %{ENV:REDIRECT_VAR} trong thành phần giá trị của một lá cờ RewriteRuleE (ví dụ [E=VAR:%{ENV:REDIRECT_VAR}]) không xuất hiện để được documented:

VAL có thể chứa backreferences ($ N hoặc% N) sẽ được mở rộng.

Nó xuất hiện để làm việc, nhưng nếu bạn muốn tránh dựa vào một cái gì đó không có cơ sở (hãy sửa lại cho tôi nếu tôi sai về điều đó), nó có thể dễ dàng được thực hiện theo cách này thay vì:

RewriteCond %{ENV:REDIRECT_VAR} (.+) 
RewriteRule .* - [E=VAR:%1] 

SetEnvIf

Tôi không khuyên bạn nên dựa vào điều này, vì nó dường như không nhất quán với số documented behavior (xem bên dưới), nhưng điều này (trong docroot/.htaccess, với Apache 2.2.20) làm việc cho tôi:

SetEnvIf REDIRECT_VAR (.+) VAR=$1 

Chỉ những biến môi trường được xác định bởi SetEnvIf [NoCase] ​​chỉ thị trước đó có sẵn để thử nghiệm theo cách này.

Tại sao?

Tôi không biết lý do để đặt tiền tố cho những tên này với số REDIRECT_ là gì - không đáng ngạc nhiên vì nó dường như không được đề cập trong phần tài liệu Apache cho mod_rewrite directives, RewriteRule flags hoặc environment variables.

Hiện tại nó có vẻ như là một mối phiền toái lớn đối với tôi, trong trường hợp không có lời giải thích vì sao nó tốt hơn là để lại tên được chỉ định một mình. Việc thiếu tài liệu chỉ góp phần vào sự hoài nghi của tôi về nó.

Có thể gán các biến môi trường trong quy tắc viết lại rất hữu ích, hoặc ít nhất, nó sẽ là. Nhưng tính hữu dụng bị giảm đi rất nhiều bởi hành vi thay đổi tên này. Sự phức tạp của bài viết này minh họa cách thức hoạt động của hành vi này và các vòng lặp phải vượt qua để cố gắng vượt qua nó.

+0

Đến trên một quy tắc khác thuộc thể loại gây khó chịu không có giấy tờ. Trên một số máy chủ, tên biến môi trường phải được bắt đầu bằng HTTP_. (http://stackoverflow.com/questions/17073144/environment-variables-are-not-passed-from-htaccess-to-php) – Chris

+0

Xin lỗi, tôi không hiểu một điều. Trong ví dụ của bạn nếu các quy tắc là: 'RewriteRule^A \ .php sub/B.php [L]' và sau đó 'RewriteRule. * - [E = MAJOR: flaw]', nếu quy tắc đầu tiên khớp, 'MAJOR 'sẽ không được đặt khiến nguyên tắc đầu tiên phù hợp và là cờ cuối cùng (cờ L). URL được viết lại và apache lặp lại qua vòng lặp bên trong 'mod_rewrite' và lần này khớp với' RewriteRule. * - [E = MAJOR: flaw] ', đặt biến môi trường và chỉ sau đó đi sau' .htaccess tiếp theo 'inside'/sub'. Nó có đúng không? – tonix

+0

@tonix Chỉ cần đi từ những gì tôi đã viết ở đây, không, điều đó không đúng. Cách tôi mô tả nó, 'RewriteRule' đầu tiên viết lại URL thành' sub/B.php' và sau đó là trò chơi trên 'docroot/.htaccess': biến env chưa bao giờ thực sự được thiết lập. (Mô tả rằng như biến env "biến mất" có thể không phải là cách tốt nhất để đặt nó.) Một lần nữa, đó là nếu có một 'phụ/.htaccess' có chứa bất kỳ chỉ thị liên quan viết lại. – JMM

Các vấn đề liên quan