it-swarm-vi.tech

Nhận PHP để dừng thay thế các ký tự '.' Trong mảng $ _GET hoặc $ _POST?

Nếu tôi vượt qua PHP biến với . trong tên của họ thông qua $ _GET PHP tự động thay thế chúng bằng _ nhân vật. Ví dụ:

<?php
echo "url is ".$_SERVER['REQUEST_URI']."<p>";
echo "x.y is ".$_GET['x.y'].".<p>";
echo "x_y is ".$_GET['x_y'].".<p>";

... xuất ra như sau:

url is /SpShipTool/php/testGetUrl.php?x.y=a.b
x.y is .
x_y is a.b.

... câu hỏi của tôi là thế này: có bất kỳ cách nào tôi có thể khiến điều này dừng lại? Không thể cho cuộc sống của tôi tìm ra những gì tôi đã làm để xứng đáng với điều này

Phiên bản PHP tôi đang chạy là 5.2.4-2ubfox5.3.

70
Dave Carpeneto

Dưới đây là lời giải thích của PHP.net về lý do tại sao nó làm điều đó:

Dấu chấm trong tên biến đến

Thông thường, PHP không thay đổi tên của các biến khi chúng được chuyển vào tập lệnh. Tuy nhiên, cần lưu ý rằng dấu chấm (dấu chấm, dấu chấm hết) không phải là ký tự hợp lệ trong a = PHP tên biến. Vì lý do, hãy xem nó:

<?php
$varname.ext;  /* invalid variable name */
?>

Bây giờ, những gì trình phân tích cú pháp nhìn thấy là một biến có tên $ varname, theo sau là toán tử nối chuỗi, theo sau là chuỗi chặn (tức là chuỗi không trích dẫn không khớp với bất kỳ từ khóa hoặc từ dành riêng nào) 'ext'. Rõ ràng, điều này không có kết quả dự định.

Vì lý do này, điều quan trọng cần lưu ý là PHP sẽ tự động thay thế bất kỳ dấu chấm nào trong tên biến đến bằng dấu gạch dưới.

Đó là từ http://ca.php.net/variabled.external .

Ngoài ra, theo nhận xét này những ký tự khác này được chuyển đổi thành dấu gạch dưới:

Danh sách đầy đủ các ký tự tên trường mà PHP chuyển đổi thành _ (gạch dưới) là như sau (không chỉ là dấu chấm):

  • chr (32) () (không gian)
  • chr (46) (.) (dấu chấm)
  • chr (91) ([) (khung vuông mở)
  • chr (128) - chr (159) (khác nhau)

Vì vậy, có vẻ như bạn bị mắc kẹt với nó, vì vậy bạn sẽ phải chuyển đổi dấu gạch dưới thành dấu chấm trong tập lệnh của mình bằng cách sử dụng gợi ý của dawnerd (Tôi chỉ sử dụng str numplace Tuy nhiên.)

63
Jeremy Ruten

Từ lâu đã trả lời câu hỏi, nhưng thực sự có một câu trả lời tốt hơn (hoặc giải quyết vấn đề). PHP cho phép bạn tại luồng đầu vào thô , vì vậy bạn có thể làm một cái gì đó như thế này:

$query_string = file_get_contents('php://input');

sẽ cung cấp cho bạn mảng $ _POST ở định dạng chuỗi truy vấn, theo chu kỳ.

Sau đó, bạn có thể phân tích cú pháp nếu bạn cần (theo bình luận của POST )

<?php
// Function to fix up PHP's messing up input containing dots, etc.
// `$source` can be either 'POST' or 'GET'
function getRealInput($source) {
    $pairs = explode("&", $source == 'POST' ? file_get_contents("php://input") : $_SERVER['QUERY_STRING']);
    $vars = array();
    foreach ($pairs as $pair) {
        $nv = explode("=", $pair);
        $name = urldecode($nv[0]);
        $value = urldecode($nv[1]);
        $vars[$name] = $value;
    }
    return $vars;
}

// Wrapper functions specifically for GET and POST:
function getRealGET() { return getRealInput('GET'); }
function getRealPOST() { return getRealInput('POST'); }
?>

Rất hữu ích cho các tham số OpenID, chứa cả '.' và '_', mỗi ý nghĩa nhất định!

57
crb

Làm nổi bật một câu trả lời thực tế của Johan trong một bình luận ở trên - Tôi chỉ gói toàn bộ bài viết của mình trong một mảng cấp cao nhất mà hoàn toàn bỏ qua vấn đề mà không cần xử lý nặng.

Theo mẫu bạn làm

<input name="data[database.username]">  
<input name="data[database.password]">  
<input name="data[something.else.really.deep]">  

thay vì

<input name="database.username"> 
<input name="database.password"> 
<input name="something.else.really.deep">  

và trong trình xử lý bài, chỉ cần mở khóa:

$posdata = $_POST['data'];

Đối với tôi đây là một sự thay đổi hai dòng, vì quan điểm của tôi hoàn toàn được đặt ra.

FYI. Tôi đang sử dụng dấu chấm trong tên trường của mình để chỉnh sửa cây dữ liệu được nhóm.

26
scipilot

Hoạt động của chức năng này là một bản hack thiên tài mà tôi đã nghĩ ra trong kỳ nghỉ hè năm 2013. Tôi sẽ viết một bài đăng trên blog về nó vào một ngày nào đó.

Khắc phục sự cố này hoạt động phổ biến và có hỗ trợ mảng sâu, ví dụ a.a[x][b.a]=10. Nó sử dụng parse_str() đằng sau hậu trường với một số tiền xử lý.

function fix($source) {
    $source = preg_replace_callback(
        '/(^|(?<=&))[^=[&]+/',
        function($key) { return bin2hex(urldecode($key[0])); },
        $source
    );

    parse_str($source, $post);

    $result = array();
    foreach ($post as $key => $val) {
        $result[hex2bin($key)] = $val;
    }
    return $result;
}

Và sau đó bạn có thể gọi hàm này như thế này, tùy thuộc vào nguồn:

$_POST   = fix(file_get_contents('php://input'));
$_GET    = fix($_SERVER['QUERY_STRING']);
$_COOKIE = fix($_SERVER['HTTP_COOKIE']);

Dành cho PHP dưới 5,4 : sử dụng base64_encode Thay vì bin2hexbase64_decode Thay vì hex2bin.

18
Rok Kralj

Điều này xảy ra bởi vì một dấu chấm là một ký tự không hợp lệ trong tên của một biến, reason mà nó nằm rất sâu trong việc triển khai PHP, do đó không có bản sửa lỗi dễ dàng nào (chưa).

Trong thời gian chờ đợi, bạn có thể giải quyết vấn đề này bằng cách:

  1. Truy cập dữ liệu truy vấn thô thông qua php://input Cho POST dữ liệu hoặc $_SERVER['QUERY_STRING'] Cho dữ liệu GET
  2. Sử dụng chức năng chuyển đổi.

Hàm chuyển đổi bên dưới (PHP> = 5.4) mã hóa tên của từng cặp khóa-giá trị thành biểu diễn thập lục phân và sau đó thực hiện một parse_str(); Sau khi hoàn thành, nó hoàn nguyên các tên thập lục phân trở lại dạng ban đầu của chúng:

function parse_qs($data)
{
    $data = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function($match) {
        return bin2hex(urldecode($match[0]));
    }, $data);

    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

// work with the raw query string
$data = parse_qs($_SERVER['QUERY_STRING']);

Hoặc là:

// handle posted data (this only works with application/x-www-form-urlencoded)
$data = parse_qs(file_get_contents('php://input'));
6
Ja͢ck

Cách tiếp cận này là một phiên bản đã thay đổi của Rok Kralj, nhưng với một số điều chỉnh để hoạt động, để cải thiện hiệu quả (tránh các cuộc gọi lại không cần thiết, mã hóa và giải mã trên các phím không bị ảnh hưởng) và xử lý chính xác các phím mảng.

A Gist with tests có sẵn và mọi phản hồi hoặc đề xuất đều được chào đón ở đây hoặc ở đó.

public function fix(&$target, $source, $keep = false) {                        
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    $keys = array();                                                           

    $source = preg_replace_callback(                                           
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        function ($key) use (&$keys) {                                         
            $keys[] = $key = base64_encode(urldecode($key[0]));                
            return urlencode($key);                                            
        },                                                                     
    $source                                                                    
    );                                                                         

    if (!$keep) {                                                              
        $target = array();                                                     
    }                                                                          

    parse_str($source, $data);                                                 
    foreach ($data as $key => $val) {                                          
        // Only unprocess encoded keys                                      
        if (!in_array($key, $keys)) {                                          
            $target[$key] = $val;                                              
            continue;                                                          
        }                                                                      

        $key = base64_decode($key);                                            
        $target[$key] = $val;                                                  

        if ($keep) {                                                           
            // Keep a copy in the underscore key version                       
            $key = preg_replace('/(\.| )/', '_', $key);                        
            $target[$key] = $val;                                              
        }                                                                      
    }                                                                          
}                                                                              
5
El Yobo

Lý do điều này xảy ra là do chức năng register_globals cũ của PHP. Các . ký tự không phải là ký tự hợp lệ trong tên biến, vì vậy PHP bao trùm nó thành dấu gạch dưới để đảm bảo có tính tương thích.

Nói tóm lại, đó không phải là một cách thực hành tốt để thực hiện các giai đoạn trong các biến URL.

4
Jeremy Privett

Nếu tìm kiếm bất kỳ cách nào để nghĩa đen get PHP để dừng thay thế '. 'Các ký tự trong mảng $ _GET hoặc $ _POST, sau đó, một cách như vậy là sửa đổi nguồn của PHP (và trong trường hợp này là tương đối đơn giản).

CẢNH BÁO: Sửa đổi PHP Nguồn C là một tùy chọn nâng cao !

Cũng xem điều này báo cáo lỗi PHP trong đó đề xuất sửa đổi tương tự.

Để khám phá, bạn cần phải:

  • tải xuống mã nguồn C của PHP
  • vô hiệu hóa kiểm tra thay thế .
  • ./configure, make và triển khai bản dựng PHP tùy chỉnh của bạn

Bản thân thay đổi nguồn là không đáng kể và liên quan đến việc cập nhật chỉ một nửa của một dòng trong main/php_variables.c:

....
/* ensure that we don't have spaces or dots in the variable name (not binary safe) */
for (p = var; *p; p++) {
    if (*p == ' ' /*|| *p == '.'*/) {
        *p='_';
....

Lưu ý: so với bản gốc || *p == '.' Đã được nhận xét


Kết quả ví dụ:

được cung cấp QUERY_STRING của a.a[]=bb&a.a[]=BB&c%20c=dd, đang chạy <?php print_r($_GET); hiện tạo:

[.__.] Mảng [.__.] ([.__.] [Aa] => Mảng [.__.] ([.__.] [0] => bb [.__.] [1] => BB [.___.]) [.__.] [.__.] [C_c] => dd [.__.]) [.__.]

Ghi chú :

  • bản vá này chỉ giải quyết câu hỏi ban đầu (nó dừng thay thế dấu chấm, không phải dấu cách).
  • chạy trên bản vá này sẽ nhanh hơn các giải pháp ở cấp độ tập lệnh, nhưng những câu trả lời thuần .php đó vẫn thường được ưa thích hơn (vì chúng tránh thay đổi PHP).
  • về lý thuyết, cách tiếp cận polyfill có thể có ở đây và có thể kết hợp các cách tiếp cận - kiểm tra thay đổi cấp độ C bằng cách sử dụng parse_str() và (nếu không có) quay lại các phương thức chậm hơn.
3
humbletim

Sau khi xem giải pháp của Rok, tôi đã tìm ra một phiên bản giải quyết những hạn chế trong câu trả lời của tôi dưới đây, giải pháp của crb ở trên và của Rok. Xem một phiên bản cải tiến của tôi .


@ crb's answer ở trên là một khởi đầu tốt, nhưng có một vài vấn đề.

  • Nó xử lý lại tất cả mọi thứ, đó là quá mức cần thiết; chỉ những trường có dấu "." trong tên cần phải được xử lý lại.
  • Không thể xử lý các mảng theo cùng cách mà quá trình gốc PHP xử lý, ví dụ: đối với các khóa như "foo.bar []".

Giải pháp dưới đây giải quyết cả hai vấn đề này ngay bây giờ (lưu ý rằng nó đã được cập nhật kể từ khi được đăng lần đầu). Điều này nhanh hơn khoảng 50% so với câu trả lời của tôi ở trên trong thử nghiệm của tôi, nhưng sẽ không xử lý các tình huống trong đó dữ liệu có cùng khóa (hoặc khóa được trích xuất giống nhau, ví dụ: foo.bar và foo_bar đều được trích xuất dưới dạng foo_bar).

<?php

public function fix2(&$target, $source, $keep = false) {                       
    if (!$source) {                                                            
        return;                                                                
    }                                                                          
    preg_match_all(                                                            
        '/                                                                     
        # Match at start of string or &                                        
        (?:^|(?<=&))                                                           
        # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
        [^=&\[]*                                                               
        # Affected cases: periods and spaces                                   
        (?:\.|%20)                                                             
        # Keep matching until assignment, next variable, end of string or   
        # start of an array                                                    
        [^=&\[]*                                                               
        /x',                                                                   
        $source,                                                               
        $matches                                                               
    );                                                                         

    foreach (current($matches) as $key) {                                      
        $key    = urldecode($key);                                             
        $badKey = preg_replace('/(\.| )/', '_', $key);                         

        if (isset($target[$badKey])) {                                         
            // Duplicate values may have already unset this                    
            $target[$key] = $target[$badKey];                                  

            if (!$keep) {                                                      
                unset($target[$badKey]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              
2
El Yobo

Giải pháp của tôi cho vấn đề này là nhanh chóng và bẩn thỉu, nhưng tôi vẫn thích nó. Tôi chỉ đơn giản muốn đăng một danh sách tên tệp đã được kiểm tra trên biểu mẫu. Tôi đã sử dụng base64_encode để mã hóa tên tệp trong phần đánh dấu và sau đó chỉ giải mã nó bằng base64_decode trước khi sử dụng chúng.

2
Jason

Giải pháp hiện tại của tôi (dựa trên trả lời chủ đề trước):

function parseQueryString($data)
{
    $data = rawurldecode($data);   
    $pattern = '/(?:^|(?<=&))[^=&\[]*[^=&\[]*/';       
    $data = preg_replace_callback($pattern, function ($match){
        return bin2hex(urldecode($match[0]));
    }, $data);
    parse_str($data, $values);

    return array_combine(array_map('hex2bin', array_keys($values)), $values);
}

$_GET = parseQueryString($_SERVER['QUERY_STRING']);
0
sasha-ch

Sử dụng crb's tôi muốn tạo lại $_POST toàn bộ mảng mặc dù vậy, hãy nhớ rằng bạn vẫn phải đảm bảo bạn mã hóa và giải mã chính xác cả ở máy khách và máy chủ. Điều quan trọng là phải hiểu khi một nhân vật là thật sự không hợp lệ và nó thực sự hợp lệ. Ngoài ra, mọi người nên stillluôn luôn thoát dữ liệu khách hàng trước khi sử dụng với any lệnh cơ sở dữ liệu không có ngoại lệ.

<?php
unset($_POST);
$_POST = array();
$p0 = explode('&',file_get_contents('php://input'));
foreach ($p0 as $key => $value)
{
 $p1 = explode('=',$value);
 $_POST[$p1[0]] = $p1[1];
 //OR...
 //$_POST[urldecode($p1[0])] = urldecode($p1[1]);
}
print_r($_POST);
?>

Tôi khuyên bạn chỉ nên sử dụng điều này cho các trường hợp riêng lẻ, tôi không chắc chắn về những điểm tiêu cực của việc đặt nó ở đầu tệp tiêu đề chính của bạn.

0
John

Chà, hàm tôi bao gồm bên dưới, "getRealPostArray ()", không phải là một giải pháp hay, nhưng nó xử lý các mảng và hỗ trợ cả hai tên: "alpha_beta" và "alpha.beta":

  <input type='text' value='First-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='Second-.' name='alpha.beta[a.b][]' /><br>
  <input type='text' value='First-_' name='alpha_beta[a.b][]' /><br>
  <input type='text' value='Second-_' name='alpha_beta[a.b][]' /><br>

trong khi var_dump ($ _ POST) tạo ra:

  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=4)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
          2 => string 'First-_' (length=7)
          3 => string 'Second-_' (length=8)

var_dump (getRealPostArray ()) tạo ra:

  'alpha.beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-.' (length=7)
          1 => string 'Second-.' (length=8)
  'alpha_beta' => 
    array (size=1)
      'a.b' => 
        array (size=2)
          0 => string 'First-_' (length=7)
          1 => string 'Second-_' (length=8)

Các chức năng, cho những gì nó có giá trị:

function getRealPostArray() {
  if ($_SERVER['REQUEST_METHOD'] !== 'POST') {#Nothing to do
      return null;
  }
  $neverANamePart = '~#~'; #Any arbitrary string never expected in a 'name'
  $postdata = file_get_contents("php://input");
  $post = [];
  $rebuiltpairs = [];
  $postraws = explode('&', $postdata);
  foreach ($postraws as $postraw) { #Each is a string like: 'xxxx=yyyy'
    $keyvalpair = explode('=',$postraw);
    if (empty($keyvalpair[1])) {
      $keyvalpair[1] = '';
    }
    $pos = strpos($keyvalpair[0],'%5B');
    if ($pos !== false) {
      $str1 = substr($keyvalpair[0], 0, $pos);
      $str2 = substr($keyvalpair[0], $pos);
      $str1 = str_replace('.',$neverANamePart,$str1);
      $keyvalpair[0] = $str1.$str2;
    } else {
      $keyvalpair[0] = str_replace('.',$neverANamePart,$keyvalpair[0]);
    }
    $rebuiltpair = implode('=',$keyvalpair);
    $rebuiltpairs[]=$rebuiltpair;
  }
  $rebuiltpostdata = implode('&',$rebuiltpairs);
  parse_str($rebuiltpostdata, $post);
  $fixedpost = [];
  foreach ($post as $key => $val) {
    $fixedpost[str_replace($neverANamePart,'.',$key)] = $val;
  }
  return $fixedpost;
}
0
ChrisNY