الحصول على PHP لوقف استبدال '.' الأحرف في $_GET أو $_POST المصفوفات?

StackOverflow https://stackoverflow.com/questions/68651

  •  09-06-2019
  •  | 
  •  

سؤال

إذا كنت تمرير متغيرات PHP مع . في أسمائهم عبر $_GET 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's تفسير لماذا يفعل ذلك:

النقاط الواردة في أسماء المتغيرات

عادة, PHP لا يغير أسماء المتغيرات عندما مرت في البرنامج النصي.ومع ذلك ، وتجدر الإشارة إلى أن نقطة (الفترة ، وقف كامل) ليست حرف صحيح في PHP اسم المتغير.ولهذا السبب ، انظر في ذلك:

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

الآن, ما محلل يرى هو متغير اسمه $varname ، تليها سلسلة سلسلة المشغل ، تليها على barestring (أيالمسعرة سلسلة التي لا تتطابق مع أي مفتاح أو الكلمات المحجوزة) تحويلة.ومن الواضح أن هذا لا يجب النتيجة المرجوة.

لهذا السبب ، من المهم أن علما بأن 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 مجموعة في سلسلة الاستعلام شكل فترات كما ينبغي أن تكون.

ثم يمكنك تحليل ذلك إذا كنت بحاجة (كما في ملصق تعليق)

<?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 المعلمات التي تحتوي على '.' و ' _ ' ، كل منها معنى معين!

تسليط الضوء على لجواب يوهان في التعليق أعلاه - فقط ملفوفة منصبي بأكمله في أعلى مستوى الصفيف الذي يتجاوز تماما مشكلة مع أي ثقيلة المعالجة المطلوبة.

في شكل تفعل

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

PHP أدناه 5.4: استخدام base64_encode بدلا من bin2hex و base64_decode بدلا من hex2bin.

يحدث هذا لأن فترة هو حرف غير صالح في متغير اسمه ، السبب التي تكمن عميقة جدا في تنفيذ PHP, حتى لا تكون هناك إصلاحات سهلة (حتى الآن).

في هذه الأثناء يمكنك العمل مع هذه المشكلة:

  1. الوصول إلى الخام الاستعلام عن البيانات إما عن طريق php://input بعد البيانات أو $_SERVER['QUERY_STRING'] من أجل الحصول على البيانات
  2. باستخدام وظيفة التحويل.

أدناه التحويل وظيفة (PHP >= 5.4) بترميز أسماء كل زوج قيمة المفتاح إلى تمثيل عشري ثم ينفذ العادية parse_str();فعلت مرة واحدة, فإنه يعود الست عشري أسماء مرة أخرى إلى شكلها الأصلي:

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

هذا النهج هو نسخة معدلة من كوريا Kralj ، ولكن مع بعض التغيير والتبديل إلى العمل على تحسين الكفاءة (تجنب لا لزوم لها رد, فك التشفير والترميز على تتأثر مفاتيح) و التعامل بشكل صحيح مع مجموعة مفاتيح.

A جوهر مع الاختبارات يتوفر أي ملاحظات أو اقتراحات هي موضع ترحيب هنا أو هناك.

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 ج المصدر هو خيار متقدم!

انظر أيضا هذا 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); تنتج الآن:

Array
(
    [a.a] => Array
        (
            [0] => bb
            [1] => BB
        )

    [c_c] => dd
)

ملاحظات:

  • هذا التصحيح عناوين السؤال الأصلي فقط (توقف استبدال النقاط ، لا مسافات).
  • يعمل على هذا التصحيح سوف يكون أسرع من البرنامج النصي على مستوى الحلول ، ولكن هذه pure-.php إجابات لا تزال عموما-من الأفضل (لأنها تجنب تغيير PHP نفسها).
  • في نظرية polyfill النهج هو ممكن هنا يمكن الجمع بين النهج -- اختبار مستوى C-تغيير استخدام parse_str() و (إذا كان متوفر) التراجع إلى تباطؤ الأساليب.

الحل لهذه المشكلة سريعة وقذرة, ولكن ما زلت أحب ذلك.ببساطة أردت أن نشر قائمة أسماء الملفات التي تم فحصها في النموذج.اعتدت base64_encode لترميز الملفات داخل العلامات و من ثم فك الشفرة مع base64_decode قبل استخدامها.

بعد النظر في كوريا الحل وقد تأتي مع الإصدار الذي يتناول القيود في جوابي أدناه ، crb فوق و كوريا الحل كذلك.ترى بلدي نسخة محسنة.


@crb الجواب أعلاه هو بداية جيدة ، ولكن هناك عددا من المشاكل.

  • فإنه reprocesses كل شيء, وهي مبالغة;فقط تلك الحقول التي تحتوي على "." في اسم تحتاج إلى إعادة تصنيعها.
  • أنه فشل في التعامل مع المصفوفات بنفس الطريقة أن الأم PHP المعالجة لا, مثلمفاتيح مثل "فو.شريط[]".

الحل تحت عناوين كل من هذه المشاكل الآن (لاحظ أنه قد تم تحديثها منذ نشرت أصلا).هذا هو حوالي 50 ٪ أسرع من جوابي أعلاه في بلدي التجارب ، ولكن لن التعامل مع الحالات التي تكون فيها البيانات نفس المفتاح (أو مفتاح الذي يحصل استخراج نفسه, على سبيل المثالفو.شريط 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" و "ألفا.بيتا":

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

باستخدام crb ما أردت إعادة $_POST مجموعة ككل على الرغم من أن نضع في اعتبارنا سوف لا يزال لديك للتأكد من أنك ترميز وفك ترميز بشكل صحيح سواء في الملقم والعميل.من المهم أن نفهم عندما يكون حرف حقا صالح و هو حقا صالح.بالإضافة إلى ذلك يجب على الناس لا يزال و دائما الهروب بيانات العميل قبل استخدامه مع أي الأمر قاعدة البيانات دون استثناء.

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

لا نوصي باستخدام هذا فقط على الحالات الفردية فقط ، مرتجلا أنا غير متأكد حول النقاط السلبية من وضع هذا في أعلى الأولية رأس الملف.

بلدي الحل الحالي (على أساس الموضوع السابق الردود):

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']);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top