code

'.'대체를 중지하도록 PHP를 가져옵니다.

codestyles 2020. 10. 31. 09:45
반응형

'.'대체를 중지하도록 PHP를 가져옵니다. $ _GET 또는 $ _POST 배열의 문자?


.$ _GET을 통해 이름에 PHP 변수를 전달하면 PHP 는 해당 변수 _문자로 자동 대체합니다 . 예를 들면 :

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

... 다음을 출력합니다.

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

... 제 질문은 이것입니다. 이걸 멈출 수 있는 방법이 있습니까? 내 인생을 위해 내가이 자격을 얻기 위해 뭘했는지 알아낼 수 없어

내가 실행중인 PHP 버전은 5.2.4-2ubuntu5.3입니다.


다음은 PHP.net의 설명입니다.

들어오는 변수 이름의 점

일반적으로 PHP는 변수가 스크립트로 전달 될 때 변수 이름을 변경하지 않습니다. 그러나 점 (마침표, 마침표)은 PHP 변수 이름에서 유효한 문자가 아닙니다. 그 이유는 다음과 같습니다.

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

이제 파서가 보는 것은 $ varname이라는 변수와 문자열 연결 연산자, 베어 스트링 (즉, 알려진 키나 예약어와 일치하지 않는 인용되지 않은 문자열) 'ext'가 뒤 따릅니다. 분명히 이것은 의도 한 결과가 아닙니다.

이러한 이유로 PHP는 들어오는 변수 이름의 모든 점을 밑줄로 자동으로 대체한다는 점에 유의하는 것이 중요합니다.

http://ca.php.net/variables.external 에서 가져온 것 입니다.

또한 이 주석에 따르면 이러한 다른 문자는 밑줄로 변환됩니다.

PHP가 _ (밑줄)로 변환하는 필드 이름 문자의 전체 목록은 다음과 같습니다 (단지 점이 아님).

  • chr (32) () (공백)
  • chr (46) (.) (점)
  • chr (91) ([) (여는 대괄호)
  • chr (128)-chr (159) (다양 함)

그래서 당신이 그것에 붙어있는 것처럼 보이므로 dawnerd의 제안을 사용하여 밑줄을 다시 점으로 변환해야합니다 ( 하지만 str_replace를 사용하겠습니다 .)


오래 전부터 대답 한 질문이지만 실제로 더 나은 대답 (또는 해결 방법)이 있습니다. PHP를 사용하면 원시 입력 스트림 에서 다음과 같이 할 수 있습니다.

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

그러면 쿼리 문자열 형식으로 $ _POST 배열이 제공됩니다.

그런 다음 필요한 경우 구문 분석 할 수 있습니다 ( POSTer의 의견에 따라 ).

<?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'); }
?>

'.'를 모두 포함하는 OpenID 매개 변수에 매우 유용합니다. 그리고 '_'는 각각 특정한 의미를 가지고 있습니다!


위의 주석에서 Johan의 실제 답변을 강조 표시-나는 무거운 처리가 필요하지 않고 문제를 완전히 우회하는 최상위 배열로 전체 게시물을 래핑했습니다.

당신이하는 형태로

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

대신에

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

그리고 포스트 핸들러에서 그냥 풀면됩니다 :

$posdata = $_POST['data'];

제 견해는 완전히 템플릿 화 되었기 때문에 이것은 두 줄의 변화였습니다.

참고로. 그룹화 된 데이터의 트리를 편집하기 위해 필드 이름에 점을 사용하고 있습니다.


이 기능의 작동은 내가 2013 년 여름 방학 동안 생각 해낸 천재적인 핵이다. 언젠가 블로그에 글을 쓸 것이다.

이 수정은 보편적으로 작동하며 예를 들어 a.a[x][b.a]=10. parse_str()일부 전처리와 함께 무대 뒤에서 사용합니다 .

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;
}

그런 다음 소스에 따라 다음과 같이이 함수를 호출 할 수 있습니다.

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

5.4 아래의 PHP의 경우 : 사용하는 base64_encode대신에 bin2hex하고 base64_decode대신 hex2bin.


이것은 마침표가 변수 이름에서 유효하지 않은 문자이기 때문에 발생합니다. 그 이유 는 PHP 구현에 매우 깊숙이 있기 때문에 (아직) 쉬운 수정이 없습니다.

그동안 다음과 같은 방법으로이 문제를 해결할 수 있습니다.

  1. php://inputPOST 데이터 또는 $_SERVER['QUERY_STRING']GET 데이터 를 통해 원시 쿼리 데이터에 액세스
  2. 변환 기능 사용.

아래 변환 함수 (PHP> = 5.4)는 각 키-값 쌍의 이름을 16 진수 표현으로 인코딩 한 다음 정규식을 수행합니다 parse_str(). 완료되면 16 진수 이름을 원래 형식으로 되돌립니다.

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']);

또는:

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

이 접근 방식은 Rok Kralj의 변경된 버전이지만 효율성을 개선하고 (불필요한 콜백, 영향을받지 않는 키에 대한 인코딩 및 디코딩 방지) 배열 키를 올바르게 처리하기 위해 약간의 조정이 필요합니다.

테스트 가 포함 요점을 사용할 수 있으며 여기 또는 거기에서 피드백이나 제안을 환영합니다.

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;                                              
        }                                                                      
    }                                                                          
}                                                                              

이것이 발생하는 이유는 PHP의 오래된 register_globals 기능 때문입니다. . 문자는 변수 이름에서 유효한 문자가 아니므로 PHP는 호환성을 확인하기 위해 밑줄로 변환합니다.

간단히 말해 URL 변수에 마침표를 사용하는 것은 좋지 않습니다.


말 그대로 PHP가 '.'대체를 중지 하도록 하는 방법을 찾고 있다면 $ _GET 또는 $ _POST 배열의 문자를 사용하는 경우 이러한 방법 중 하나는 PHP의 소스를 수정하는 것입니다 (이 경우 비교적 간단합니다).

경고 : PHP C 소스 수정은 고급 옵션입니다!

또한 동일한 수정을 제안하는 PHP 버그 보고서참조하십시오 .

탐색하려면 다음이 필요합니다.

  • PHP의 C 소스 코드 다운로드
  • .교체 수표 비활성화
  • ./configure , 사용자 정의 된 PHP 빌드 작성 및 배포

소스 변경 자체는 사소한 그냥 업데이트 포함 반 한 줄의 에서 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='_';
....

참고 : 원본과 비교 || *p == '.'는 주석 처리되었습니다.


출력 예 :

의 QUERY_STRING이 주어지면 이제 a.a[]=bb&a.a[]=BB&c%20c=dd실행 <?php print_r($_GET);하면 다음이 생성됩니다.

정렬
(
    [aa] => 어레이
        (
            [0] => bb
            [1] => BB
        )

    [c_c] => dd
)

메모:

  • 이 패치는 원래 질문 만 해결합니다 (공백이 아닌 점 교체를 중지 함).
  • 이 패치에서 실행하는 것은 스크립트 수준의 솔루션보다 빠르지 만, 이러한 pure-.php 답변은 여전히 ​​일반적으로 선호됩니다 (PHP 자체를 변경하지 않기 때문에).
  • 이론적으로 polyfill 접근 방식이 여기에서 가능하며 접근 방식을 결합 parse_str()할 수 있습니다.

이 문제에 대한 나의 해결책은 빠르고 더럽지 만 여전히 마음에 듭니다. 양식에서 확인한 파일 이름 목록을 게시하고 싶었습니다. 내가 사용하는 base64_encode마크 업 내에서 파일 이름을 인코딩하고 단지 그것을 디코딩 base64_decode을 사용하기 전에.


Rok의 솔루션을 살펴본 후 아래의 내 대답, crb의 위 및 Rok의 솔루션의 한계를 해결하는 버전을 생각해 냈습니다. 내 개선 된 버전을 참조하십시오 .


의 @crb의 대답 은 좋은 시작이지만 몇 가지 문제가 있습니다.

  • 그것은 과잉 인 모든 것을 재 처리합니다. "."가있는 필드 만 이름에서 다시 처리해야합니다.
  • 예를 들어 "foo.bar []"와 같은 키의 경우 네이티브 PHP 처리와 동일한 방식으로 배열을 처리하지 못합니다.

아래 솔루션은 현재 이러한 두 가지 문제를 모두 해결합니다 (원래 게시 된 이후로 업데이트되었습니다). 이것은 내 테스트에서 위의 대답보다 약 50 % 빠르지 만 데이터에 동일한 키가있는 상황을 처리하지 않습니다 (또는 foo.bar 및 foo_bar가 모두 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]);                                       
            }                                                                  
        }                                                                      
    }                                                                          
}                                                                              

아래에 포함 된 "getRealPostArray ()"함수는 좋은 해결책은 아니지만 배열을 처리하고 "alpha_beta"및 "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>

var_dump ($ _ POST)는 다음을 생성합니다.

  '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 ())는 다음을 생성합니다.

  '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)

가치있는 기능 :

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;
}

Using crb's I wanted to recreate the $_POST array as a whole though keep in mind you'll still have to ensure you're encoding and decoding correctly both at the client and the server. It's important to understand when a character is truly invalid and it is truly valid. Additionally people should still and always escape client data before using it with any database command without exception.

<?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);
?>

I recommend using this only for individual cases only, offhand I'm not sure about the negative points of putting this at the top of your primary header file.


My current solution (based on prev topic replies):

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']);

참고URL : https://stackoverflow.com/questions/68651/get-php-to-stop-replacing-characters-in-get-or-post-arrays

반응형