PHP_SELF đấu với PATH_INFO đấu với SCRIPT_NAME với REQUEST_URI


105

Tôi đang xây dựng một ứng dụng PHP trong CodeIgniter. CodeIgniter sẽ gửi tất cả các yêu cầu với bộ điều khiển chính: index.php. Tuy nhiên, tôi không thích nhìn thấy index.phptrong URI. Ví dụ: http://www.example.com/faq/whateversẽ định tuyến đến http://www.example.com/index.php/faq/whatever. Tôi cần một cách đáng tin cậy để tập lệnh biết địa chỉ của nó là gì, vì vậy nó sẽ biết phải làm gì với điều hướng. Tôi đã sử dụng mod_rewrite, theo tài liệu CodeIgniter.

Quy tắc như sau:

RewriteEngine on
RewriteCond $1 !^(images|inc|favicon\.ico|index\.php|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L] 

Thông thường, tôi sẽ chỉ kiểm tra php_self, nhưng trong trường hợp này là luôn luôn index.php. Tôi có thể lấy nó từ REQUEST_URI, PATH_INFOv.v., nhưng tôi đang cố gắng quyết định cái nào sẽ đáng tin cậy nhất. Có ai biết (hoặc biết nơi để tìm thấy) sự khác biệt thực sự giữa PHP_SELF, PATH_INFO, SCRIPT_NAME, và REQUEST_URI? Cảm ơn bạn đã giúp đỡ!

Lưu ý : Tôi đã phải thêm dấu cách, vì SO nhìn thấy dấu gạch dưới và làm cho nó nghiêng vì một số lý do.

Đã cập nhật : Đã sửa các khoảng trắng.

Câu trả lời:


51

Các tài liệu PHP có thể cho bạn biết sự khác biệt:

'PHP_SELF'

Tên tệp của tập lệnh hiện đang thực thi, liên quan đến gốc tài liệu. Ví dụ: $ _SERVER ['PHP_SELF'] trong một tập lệnh tại địa chỉ http://example.com/test.php/foo.bar sẽ là /test.php/foo.bar . Các __FILE__ liên tục chứa đường dẫn đầy đủ và tên tập tin của hiện tại (tức là bao gồm) tập tin. Nếu PHP đang chạy dưới dạng bộ xử lý dòng lệnh, biến này chứa tên tập lệnh kể từ PHP 4.3.0. Trước đây nó không có sẵn.

'SCRIPT_NAME'

Chứa đường dẫn của tập lệnh hiện tại. Điều này hữu ích cho các trang cần trỏ đến chính chúng. Các __FILE__ liên tục chứa đường dẫn đầy đủ và tên tập tin của hiện tại (tức là bao gồm) tập tin.

'REQUEST_URI'

URI đã được cung cấp để truy cập trang này; ví dụ: '/index.html' .

PATH_INFO dường như không được ghi lại ...


3
Đây rất có thể không phải là về tài liệu PHP mà là về CGI :) Và có PATH_INFO được ghi lại: tools.ietf.org/html/rfc3875#section-4 Nhưng có một số vấn đề đã biết mà Apache và nginx không phải lúc nào cũng đưa ra biến này.
SimonSimCity

1
Câu trả lời của Odin dưới đây bổ sung những giải thích hữu ích được bổ sung với các ví dụ. Tôi thấy khó để hiểu những gì các biến đại diện trong một bối cảnh chung với một PATH_INFO, một chuỗi truy vấn, một số chuyển hướng, một số bí danh, trên các hệ điều hành khác nhau, từ CLI vs MÁY CHỦ vv

3
-1 Chỉ là lời giải thích tại sao tôi từ chối: toàn bộ lý do tôi đến với bài đăng này là vì tài liệu không rõ ràng. Câu trả lời của Odin dưới đây cung cấp một lời giải thích rõ ràng về sự khác biệt giữa các biến này. Tôi cảm thấy rằng đó là một câu trả lời không đủ để chỉ sao chép và dán dễ dàng tìm thấy nhưng cũng không đủ tài liệu. Tôi tin rằng hầu hết mọi người đã phải truy cập tài liệu để biết về danh sách các phần tử trong biến $ _SERVER được đề cập ở trên.
dallin

229

Một số ví dụ thực tế của sự khác biệt giữa các biến:
Ví dụ 1. PHP_SELF là khác nhau từ SCRIPT_NAME chỉ khi url yêu cầu ở dạng:
http://example.com/test.php/foo/bar

[PHP_SELF] => /test.php/foo/bar
[SCRIPT_NAME] => /test.php

(đây có vẻ là trường hợp duy nhất khi PATH_INFO chứa thông tin hợp lý [PATH_INFO] => / foo / bar) Lưu ý: điều này từng khác trong một số phiên bản PHP cũ hơn (<= 5.0?).

Ví dụ 2. REQUEST_URI khác với SCRIPT_NAME khi một chuỗi truy vấn không trống được nhập:
http://example.com/test.php?foo=bar

[SCRIPT_NAME] => /test.php
[REQUEST_URI] => /test.php?foo=bar

Ví dụ 3. REQUEST_URI khác với SCRIPT_NAME khi chuyển hướng phía máy chủ có hiệu lực (ví dụ: mod_rewrite trên apache):

http://example.com/test.php

[REQUEST_URI] => /test.php
[SCRIPT_NAME] => /test2.php

Ví dụ 4. REQUEST_URI khác với SCRIPT_NAME khi xử lý lỗi HTTP bằng tập lệnh.
Sử dụng lệnh apache ErrorDocument 404 /404error.php
http://example.com/test.php

[REQUEST_URI] => /test.php
[SCRIPT_NAME] => /404error.php

Trên máy chủ IIS sử dụng các trang lỗi tùy chỉnh
http://example.com/test.php

[SCRIPT_NAME] => /404error.php
[REQUEST_URI] => /404error.php?404;http://example.com/test.php

21
+1, "Ví dụ không phải là cách để học, mà là cách duy nhất để học." - Tôi luôn phải kiểm tra lại những thứ này, nghiên cứu rất hay về lỗi 404. =)
Alix Axel

16
+1: Lần đầu tiên trong đời tôi hiểu được sự khác biệt. Họ sẽ cập nhật tài liệu PHP với câu trả lời của bạn
Marco Demaio

Ví dụ1: [SCRIPT_NAME] => /test.php/ Không được có "/" ở cuối: Ví dụ1: [SCRIPT_NAME] => /test.php Dù sao thì đó là những gì tôi thấy trong PHP 5.3.6. Ví dụ tốt đẹp.
Dawid Ohia

Bạn đúng là JohnM2, tôi hiện đã kiểm tra trên PHP 5.4 và kết quả cho URL /pinfo.php/first/second?third=fourth như sau: QUERY_STRING => third = 4th REQUEST_URI => /pinfo.php/first/second ? third = 4th SCRIPT_NAME => /pinfo.php PATH_INFO => / first / second
Odin

Tôi cũng đã thử nghiệm điều này trên 5.2.17 và không có /ở cuối SCRIPT_NAME. Điều này dường như nhất quán trong PHP 5.2-5.4 sau đó, xem xét việc chỉnh sửa câu trả lời để phản ánh điều đó.
Fabrício Matté

24

PATH_INFO chỉ khả dụng khi sử dụng htaccess như thế này:

ví dụ 1

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ index.php/$1 [L]

Vẫn như cũ

[SCRIPT_NAME] => /index.php

Nguồn gốc

http://domain.com/

[PHP_SELF]     => /index.php
[PATH_INFO] IS NOT AVAILABLE (fallback to REQUEST_URI in your script)
[REQUEST_URI]  => /
[QUERY_STRING] => 

Con đường

http://domain.com/test

[PHP_SELF]     => /index.php/test
[PATH_INFO]    => /test
[REQUEST_URI]  => /test
[QUERY_STRING] => 

Chuỗi truy vấn

http://domain.com/test?123

[PHP_SELF]     => /index.php/test
[PATH_INFO]    => /test
[REQUEST_URI]  => /test?123
[QUERY_STRING] => 123

Ví dụ 2

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ index.php?url=$1 [L,QSA]

Vẫn như cũ

[SCRIPT_NAME]  => /index.php
[PHP_SELF]     => /index.php
[PATH_INFO] IS NOT AVAILABLE (fallback to REQUEST_URI in your script)

Nguồn gốc

http://domain.com/

[REQUEST_URI]  => /
[QUERY_STRING] => 

Con đường

http://domain.com/test

[REQUEST_URI]  => /test
[QUERY_STRING] => url=test

Chuỗi truy vấn

http://domain.com/test?123

[REQUEST_URI]  => /test?123
[QUERY_STRING] => url=test&123

Ví dụ 3

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(([a-z]{2})|(([a-z]{2})/)?(.*))$ index.php/$5 [NC,L,E=LANGUAGE:$2$4]

hoặc là

RewriteRule ^([a-z]{2})(/(.*))?$ $3 [NC,L,E=LANGUAGE:$1]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^(favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ index.php/$1 [L]

Vẫn như cũ

[SCRIPT_NAME] => /index.php

Nguồn gốc

http://domain.com/

[PHP_SELF]          => /index.php
[PATH_INFO] IS NOT AVAILABLE (fallback to REQUEST_URI in your script)
[REQUEST_URI]       => /
[QUERY_STRING]      => 
[REDIRECT_LANGUAGE] IS NOT AVAILABLE

Con đường

http://domain.com/test

[PHP_SELF]          => /index.php/test
[PATH_INFO]         => /test
[REQUEST_URI]       => /test
[QUERY_STRING]      => 
[REDIRECT_LANGUAGE] => 

Ngôn ngữ

http://domain.com/en

[PHP_SELF]          => /index.php/
[PATH_INFO]         => /
[REQUEST_URI]       => /en
[QUERY_STRING]      => 
[REDIRECT_LANGUAGE] => en

Con đường ngôn ngữ

http://domain.com/en/test

[PHP_SELF]          => /index.php/test
[PATH_INFO]         => /test
[REQUEST_URI]       => /en/test
[REDIRECT_LANGUAGE] => en

Chuỗi truy vấn ngôn ngữ

http://domain.com/en/test?123

[PHP_SELF]          => /index.php/test
[PATH_INFO]         => /test
[REQUEST_URI]       => /en/test?123
[QUERY_STRING]      => 123
[REDIRECT_LANGUAGE] => en

Điều này thật tuyệt. Cảm ơn bạn đã giúp đỡ!
Hội chợ Gabriel

1
Câu trả lời này được viết theo cách gợi ý rằng chỉ ghi lại url mới có thể tạo ra path_info, nhưng tất nhiên, thông tin đường dẫn có thể được nhập trực tiếp vào URL ban đầu.

12

Đường dẫn PHP

    $_SERVER['REQUEST_URI']    = Đường dẫn web, URI được yêu cầu
    $_SERVER['PHP_SELF']    = Đường dẫn web, tệp được yêu cầu + đường dẫn thông tin
    $_SERVER['SCRIPT_NAME']    = Đường dẫn web, tệp được yêu cầu
    $_SERVER['SCRIPT_FILENAME']   = Đường dẫn tệp, tệp được yêu cầu
    __FILE__    = Đường dẫn tệp, tệp hiện tại

Ở đâu

  • Đường dẫn tập tin là một đường dẫn tập tin hệ thống như /var/www/index.php, sau khi giải quyết bí danh
  • Đường dẫn web là một đường dẫn tài liệu máy chủ như /index.phptừ http://foo.com/index.phpvà thậm chí có thể không khớp với bất kỳ tệp nào
  • Tệp hiện tại có nghĩa là tệp tập lệnh được bao gồm , không phải bất kỳ tập lệnh nào bao gồm nó
  • Tệp được yêu cầu có nghĩa là tệp scriptcomper , không phải tệp được bao gồm
  • URIyêu cầu HTTP như /index.php?foo=bar, trước khi bất kỳ URL viết lại
  • Thông tin đường dẫn là bất kỳ dữ liệu Apache bổ sung nào nằm sau tên tập lệnh nhưng trước chuỗi truy vấn

Thứ tự hoạt động

  1. Máy khách gửi cho máy chủ một yêu cầu HTTP REQUEST_URI
  2. Máy chủ thực hiện bất kỳ việc ghi lại URL nào từ các tệp .htaccess, v.v. để lấyPHP_SELF
  3. Máy chủ tách PHP_SELFthành SCRIPT_FILENAME+PATH_INFO
  4. Máy chủ thực hiện Nghị quyết bí danh và cải toàn bộ đường dẫn URL đến một đường dẫn tập tin hệ thống để có đượcSCRIPT_FILENAME
  5. Tệp tập lệnh kết quả có thể bao gồm những tệp khác, trong đó __FILE__đề cập đến đường dẫn đến tệp hiện tại

Điều này là tốt. Đây là những nhận xét của tôi. Đầu tiên, cả $ _SERVER ['SCRIPT_NAME'] và $ _SERVER ['SCRIPT_FILENAME'] đều là tên tập lệnh, ngoại trừ tên sau là sau khi bí danh được thực thi. Thứ hai, $ _SERVER ['PHP_SELF'] không phải là tập lệnh, mà là tập lệnh + thông tin đường dẫn. Một lần nữa, $ _SERVER ['SCRIPT_NAME'] là tập lệnh (trước bí danh). Cuối cùng, sẽ rất hữu ích khi biết các biến này được xác định ở giai đoạn nào, sau hoặc trước khi viết lại các quy tắc, sau hoặc trước bí danh. Hãy xem câu trả lời của tôi.

@ Dominic108 Tôi đã sửa lại câu trả lời của mình dựa trên đề xuất của bạn, thu dọn mọi thứ một chút và thêm phần Thứ tự hoạt động. Cho tôi biết bạn nghĩ gì. Cảm ơn!
Beejor

Theo thứ tự của bạn, bạn phải hoán đổi $_SERVER['SCRIPT_NAME']và   $_SERVER['PHP_SELF']bởi vì mod_rewrite tạo ra toàn bộ đường dẫn $_SERVER['PHP_SELF']. Sự phân tách xảy ra tiếp theo. Lưu ý rằng bí danh cũng xem xét toàn bộ đường dẫn để xác định tên tệp tập lệnh, nhưng sự phân tách đã xác định tên tập lệnh và đường dẫn_info đã xảy ra, vì vậy chúng sẽ không bị ảnh hưởng.

@ Dominic108 Tôi đã sửa lại câu trả lời của mình một lần nữa. Vì lý do nào đó, đề xuất chỉnh sửa của bạn đã bị từ chối, mặc dù theo tôi biết, bạn nói đúng rằng hai trong số các mặt hàng của tôi không được đặt hàng. Tôi không rành về bí danh nên tôi dựa vào kiến ​​thức chuyên môn của bạn cho phần đó. Cảm ơn một lần nữa!
Beejor

5

Bạn có thể muốn xem xét Lớp URI và sử dụng $ this-> uri-> uri_string ()

Trả về một chuỗi với URI hoàn chỉnh.

Ví dụ: nếu đây là URL đầy đủ của bạn:

http://example.com/index.php/news/local/345

Hàm sẽ trả về:

/news/local/345

Hoặc bạn có thể tận dụng các phân đoạn để xem chi tiết các khu vực cụ thể mà không cần phải đưa ra các giá trị phân tích cú pháp / regex


Cảm ơn bạn - đây là một ý tưởng hay, nhưng tôi đang sử dụng chúng trong một hook trước hệ thống sẽ cần chạy trước khi bộ điều khiển khởi động và chạy.
Eli

4

Cá nhân tôi sử dụng $REQUEST_URIvì nó tham chiếu đến URI đã nhập chứ không phải vị trí trên đĩa của máy chủ.


Nó luôn luôn là URI hoàn chỉnh?
Eli

Thông thường, bạn có thể gặp sự cố với apache trên windows, nhưng nó chỉ đối với URI mà không giải quyết được.
Xenph Yan

4

Có rất ít điều để thêm vào câu trả lời của Odin. Tôi chỉ muốn cung cấp một ví dụ hoàn chỉnh từ yêu cầu HTTP đến tệp thực tế trên hệ thống tệp để minh họa tác động của việc viết lại URL và bí danh. Trên hệ thống tệp, tập lệnh /var/www/test/php/script.php

<?php
include ("script_included.php")
?>

nơi /var/www/test/php/script_included.php

<?php
echo "REQUEST_URI: " .  $_SERVER['REQUEST_URI'] . "<br>"; 
echo "PHP_SELF: " .  $_SERVER['PHP_SELF'] . "<br>";
echo "QUERY_STRING: " .  $_SERVER['QUERY_STRING'] . "<br>";
echo "SCRIPT_NAME: " .  $_SERVER['SCRIPT_NAME'] . "<br>";
echo "PATH_INFO: " .  $_SERVER['PATH_INFO'] . "<br>";
echo "SCRIPT_FILENAME: " . $_SERVER['SCRIPT_FILENAME'] . "<br>";
echo "__FILE__ : " . __FILE__ . "<br>";  
?>

/var/www/test/.htaccess

RewriteEngine On
RewriteRule before_rewrite/script.php/path/(.*) after_rewrite/script.php/path/$1 

và tệp cấu hình Apache bao gồm bí danh

Alias /test/after_rewrite/ /var/www/test/php/

và yêu cầu http là

www.example.com/test/before_rewrite/script.php/path/info?q=helloword

Đầu ra sẽ là

REQUEST_URI: /test/before_rewrite/script.php/path/info?q=helloword
PHP_SELF: /test/after_rewrite/script.php/path/info
QUERY_STRING: q=helloword
SCRIPT_NAME: /test/after_rewrite/script.php
PATH_INFO: /path/info
SCRIPT_FILENAME: /var/www/test/php/script.php
__FILE__ : /var/www/test/php/script_included.php

Những điều sau luôn giữ

PHP_SELF = SCRIPT_NAME + PATH_INFO = full url path between domain and query string. 

Nếu không có ghi lại mod_rewrite, mod_dir, ErrorDocument hoặc bất kỳ hình thức ghi lại URL nào, chúng tôi cũng có

REQUEST_URI = PHP_SELF + ? + QUERY_STRING 

Các bí danh ảnh hưởng đến các đường dẫn tập tin hệ thống SCRIPT_FILENAME__FILE__, không phải là đường dẫn URL, được định nghĩa trước - xem ngoại lệ bên dưới. Bí danh có thể sử dụng toàn bộ đường dẫn URL, bao gồm PATH_INFO. Không thể có kết nối nào giữa SCRIPT_NAMESCRIPT_FILENAME.

Không hoàn toàn chính xác rằng bí danh không được giải quyết tại thời điểm đường dẫn URL [PHP_SELF] = [SCRIPT_NAME] + [PATH_INFO] được xác định, vì bí danh được coi là để tìm kiếm hệ thống tệp và chúng tôi biết từ ví dụ 4 trong câu trả lời của Odin rằng hệ thống tệp được tìm kiếm để xác định xem tệp có tồn tại hay không, nhưng điều này chỉ có liên quan khi tệp không được tìm thấy. Tương tự, mod_dir gọi mod_alias để tìm kiếm hệ thống tệp, nhưng điều này chỉ phù hợp nếu bạn có bí danh chẳng hạn Alias \index.php \var\www\index.phpvà yêu cầu uri là một thư mục.


Xin chào Dominic108, cảm ơn vì đã sửa đổi. Tôi nghĩ rằng sẽ hữu ích khi bao gồm thông tin viết lại. Đối với tôi nó được ngụ ý, nhưng với những người khác thì nó có thể không trực quan như vậy.
Beejor

1

Nếu bạn quên biến nào làm những gì, bạn có thể viết một đoạn script nhỏ sử dụng phpinfo () và gọi nó từ một URL với một chuỗi truy vấn. Vì các cài đặt phần mềm máy chủ hiển thị các biến mà PHP trả về nên luôn luôn là một ý kiến ​​hay để kiểm tra kết quả đầu ra của máy trong trường hợp việc ghi lại ở tệp cấu hình máy chủ gây ra kết quả khác với mong đợi. Lưu nó dưới dạng một cái gì đó như _inf0.php:

<?php
    $my_ip = '0.0.0.0';

   if($_SERVER['REMOTE_ADDR']==$my_ip){
     phpinfo();
   } else {
     //something
   }

Sau đó, bạn sẽ gọi /_inf0.php?q=500


-1

Sao lưu một giây, bạn đã thực hiện sai cách tiếp cận để bắt đầu. Tại sao không chỉ làm điều này

RewriteEngine on
RewriteCond $1 !^(images|inc|favicon\.ico|index\.php|robots\.txt)
RewriteRule ^(.*)$ /index.php?url=$1 [L]

thay thế? Sau đó lấy nó bằng$_GET['url'];


Tại sao phải phát minh lại bánh xe? Dữ liệu này dễ dàng truy cập hơn nhiều!
Kenneth

Và có thêm sự phức tạp nếu yêu cầu ban đầu được mong đợi có một chuỗi truy vấn. Ở trạng thái hiện tại, đoạn mã trên sẽ chỉ ghi đè lên chuỗi truy vấn. Nếu bạn hợp nhất các chuỗi truy vấn ( QSAcờ) thì các tham số chuỗi truy vấn có thể bị ghi đè (ví dụ: nếu bạn cần một urltham số trong yêu cầu ban đầu) hoặc tệ hơn, dễ bị tấn công XSS.
MrWhite
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.