简体   繁体   English

PHP:如何快速将键=值文件拆分为关联数组

[英]PHP: How to quickly split a key=value file into associative array

When you send a PDT transaction ID back to paypal, you get back a list of transaction data. 当您将PDT交易ID发送回Paypal时,您会获得交易数据列表。 It has SUCCESS on the first line, and then a list of key=value pairs. 它在第一行有SUCCESS,然后是键=值对的列表。 One pair per line. 每行一对。 For example: 例如:

SUCCESS
business=paypal@example.com
charset=windows-1252
custom=
first_name=Alice
handling_amount=0.00
invoice=NN0005
item_name=Bear
item_number=BEAR05
last_name=Foobar
mc_currency=SEK
mc_fee=13.00
mc_gross=250.00
payer_email=bob@example.com
payer_id=UC9DXVX7GRSTN
payer_status=unverified
payment_date=09:08:06 Oct 18, 2010 PDT
payment_fee=
payment_gross=
payment_status=Completed
payment_type=instant
protection_eligibility=Ineligible
quantity=1
receipt_id=2479-2605-1192-2880
receiver_email=paypal@example.com
receiver_id=8Y670ENTB8BY6
residence_country=NO
shipping=0.00
tax=0.00
transaction_subject=Bear
txn_id=1PH815997L239283J
txn_type=web_accept

What is a good, quick and clean way to check if the first line equals SUCCESS and then convert this into an associative array? 什么是检查第一行是否等于SUCCESS并将其转换为关联数组的好,快速和干净的方法? I am able to do it, and it works, but I'm curious if there are better or cleaner ways of doing it cause what I end up with isn't always that nice. 我能够做到,而且能奏效,但是我很好奇是否有更好或更干净的方法会导致我最终得到的结果并不总是那么好。 Notice that some of the keys don't have any value as well. 请注意,某些键也没有任何值。

So, what I would like to end up with is essentially: 因此,我最终想要得到的基本上是:

array(
    'business' => 'paypal@example.com',
    'charset' => 'windows-1252',
    'custom' => NULL,
    'first_name' => Alice,

    // And so on

);

The order doesn't matter. 订单无关紧要。

Update: Thanks for great suggestions! 更新: 感谢您的伟大建议! Testing them out now. 现在进行测试。 Splitting the string into separate lines is also part of my problem by the way. 顺便说一下,将字符串拆分为单独的行也是我的问题的一部分。 Forgot to specify that. 忘记指定了。 See that some has taken that into account and some don't. 看到有些考虑了这一点,有些没有考虑。 It can have an impact, since certain methods needs to first split into lines and then the pairs, while others can just eat the whole thing in one piece. 这可能会产生影响,因为某些方法需要先分成几行,然后再分成两对,而其他方法则可以将整个东西分成一块吃掉。

Update: I should also have mentioned that having empty ones end up as NULL would be a bonus, but probably not a requirement. 更新: 我还应该提到,将空值结尾为NULL将是一个奖励,但可能不是必须的。 They don't do that in my version either and it doesn't really matter that much. 在我的版本中,他们也没有这样做,也没有太大关系。


Benchmark results 基准测试结果

Got curious to what I should choose here, so I benchmarked how I should do different parts. 对我在这里应该选择的东西感到好奇,因此我确定了应该如何做不同部分的基准。 Had various ideas myself, got some from here and other places as well. 我本人有各种各样的想法,也从这里和其他地方得到了一些想法。 When I had found the fastest I could manage, I created a benchmark and put it to the test against all of the answers so far. 当我找到可以管理的最快速度时,我创建了一个基准,并根据迄今为止的所有答案进行了测试。 For the ones that had skipped the splitting or the checking for success I added an explode and a strpos check accordingly. 对于那些跳过拆分或成功检查的程序,我分别添加了explodestrpos检查。 I also added urldecode to all the solutions as well, except @dynamism who tackled that so nicely. 我还向所有解决方案中添加了urldecode,除了@dynamism如此出色地解决了这个问题。 Anyways, here are the results: 无论如何,这是结果:

基准测试结果

The benchmark was run using the codebench Kohana 3 module. 使用codebench Kohana 3模块运行基准测试。 Here is the benchmark code: 这是基准代码:

<?php defined('SYSPATH') or die('No direct script access.');

/**
 * Test various methods of checking for SUCCESS
 *
 * @package  PayPal
 * @category PDT
 * @author  Torleif Berger
 */
class Bench_ProcessPDT extends Codebench
{
    public $description = 'Various ways of checking that a string starts with SUCCESS';

    public $loops = 100000;


    public function bench_mine($subject)
    {
        if(strpos($subject, 'SUCCESS') === 0)
        {
            $subject = urldecode(substr($subject, 7));
            preg_match_all('/^([^=]++)=(.*+)/m', $subject, $result, PREG_PATTERN_ORDER);
            $result = array_combine($result[1], $result[2]);

            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964308#3964308
    public function bench_dynamism_substr($subject)
    {
        if(substr($subject, 0, 7) == 'SUCCESS')
        {
            $subject = substr_replace($subject, '', 0, 7);
            $subject = str_replace(array("\n", "\r", "\r\n"), '&', $subject);
            parse_str($subject, $result);

            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964308#3964308
    public function bench_dynamism_strpos($subject)
    {
        if(strpos($subject, 'SUCCESS') === 0)
        {
            $subject = substr_replace($subject, '', 0, 7);
            $subject = str_replace("\r\n", '&', $subject);
            parse_str($subject, $result);

            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964520#3964520
    public function bench_mellowsoon($subject)
    {
        $subject = urldecode($subject);

        $lines = explode("\r\n", $subject);
        $lines = array_map('trim', $lines);
        $status = array_shift($lines);
        if($status == 'SUCCESS')
        {
            $result = array();
            foreach($lines as $line)
            {
                list($key, $value) = explode('=', $line, 2);
                $result[$key] = $value;
            }
            return array(count($result), array_shift($result), array_shift($result));
        }

        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964265#3964265
    public function bench_amber($subject)
    {
        if(strpos($subject, 'SUCCESS') === 0)
        {
            $subject = explode("\r\n", urldecode($subject));
            array_shift($subject);  // Remove is empty

            $result = array();
            foreach($subject as $line)
            {
                $bits = explode('=', $line);
                $field_name = array_shift($bits);
                $field_contents = implode('=', $bits);
                $result[$field_name] = $field_contents;
            }
            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964366#3964366
    public function bench_GigaWatt($subject)
    {
        if(strpos($subject, 'SUCCESS') === 0)
        {
            $subject = explode("\r\n", urldecode($subject));

            $result = array();
            foreach($subject as $line)
            {
                if (strpos($line, "=") === FALSE)
                    continue;

                list($var, $value) = explode("=", trim($line));
                $result[$var] = empty($value) ? NULL : $value;
            }
            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964366#3964366
    public function bench_GigaWatt2($subject)
    {
        if(strpos($subject, 'SUCCESS') === 0)
        {
            $subject = explode("\r\n", urldecode($subject));

            $result = array();
            foreach($subject as $line)
            {
                if (strpos($line, "=") === FALSE)
                    continue;

                list($var, $value) = explode("=", trim($line));
                $result[$var] = $value;
            }
            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }

    // http://stackoverflow.com/questions/3964219/3964323#3964323
    public function bench_dvhh($subject)
    {
        if(strpos($subject, 'SUCCESS') === 0)
        {
            $subject = explode("\r\n", urldecode($subject));

            $result = array();
            foreach($subject as $line)
            {
                $lineData = preg_split("/\s*=\s*/", $line);
                if(count($lineData) == 2)
                {
                    $result[$lineData[0]] = $lineData[1];
                }
            }
            return array(count($result), array_shift($result), array_shift($result));
        }
        return FALSE;
    }


    public $subjects = array
    (
        "SUCCESS\r\nbusiness=paypal@business.example.com\r\ncharset=windows-1252\r\ncustom=\r\nfirst_name=Alice\r\nhandling_amount=0.00\r\ninvoice=AF000001\r\nitem_name=Stuffed bear\r\nitem_number=BEAR05\r\nlast_name=Foobar\r\nmc_currency=USD\r\nmc_fee=2.00\r\nmc_gross=20.00\r\npayer_email=alice.foobar@example.com\r\npayer_id=UC9DXVX7GRSTN\r\npayer_status=unverified\r\npayment_date=09:08:06 Oct 18, 2010 PDT\r\npayment_fee=\r\npayment_gross=\r\npayment_status=Completed\r\npayment_type=instant\r\nprotection_eligibility=Ineligible\r\nquantity=1\r\nreceipt_id=2479-2605-1192-2880\r\nreceiver_email=paypal@example.com\r\nreceiver_id=8Y670ENTB8BY6\r\nresidence_country=USD\r\nshipping=0.00\r\ntax=0.00\r\ntransaction_subject=Bear\r\ntxn_id=1PH815997L239283J\r\ntxn_type=web_accept",

        "FAIL\r\nError: 4003",

        "INVALID",
    );
}

If anyone has any further tips for improvement, please let me know :) 如果有人有进一步改进的技巧,请告诉我:)

Split off the first line separately, check it, and then use this to grab the rest: 分开分割第一行,选中它,然后使用它来抓住其余的内容:

foreach($response_lines as $line) {
    $bits = explode('=', $line);
    $field_name = array_shift($bits);
    $field_contents = implode('=', $bits);
    $fields[$field_name] = $field_contents;
}

After which $fields will be the array you seek. 之后, $fields将是您要查找的数组。 (One note: items without a value in the response will have '' instead of NULL .) (请注意:响应中没有值的项目将使用''而不是NULL 。)

This is how I'd do it in 5 lines: 这是我将在5行中执行的操作:

// Is this a successful post?
if( substr($paypal, 0, 7) == 'SUCCESS')
{
    $paypal = substr_replace($paypal, '', 0, 7);
    $paypal = str_replace(array("\n", "\r", "\r\n"), '&', $paypal);
    parse_str($paypal, $response_array);
}

Check to see if the postback $paypal is successful, and if it is remove the first line, replace newlines with & then run it through parse_str , sorted. 检查回发$paypal是否成功,如果删除了第一行,则用&替换换行符,然后通过parse_str进行排序。 Output is in $response_array ready to rock. 输出在$response_array准备就绪。

Same problem as Amber, though, where NULL is shown as '' . 但是,与Amber同样的问题,其中NULL显示为''

Edit: Also, a caveat: If the array key has any special characters in it (for some strange reason, maybe a . ) it is converted to an _ . 编辑:另外,警告:如果数组键中包含任何特殊字符(出于某些奇怪的原因,可能是. ),它将转换为_

use http://www.php.net/manual/en/function.preg-split.php 使用http://www.php.net/manual/en/function.preg-split.php

foreach($lines as $line) {
    $lineData = preg-split("\s*=\s*",$line);
    if(count($lineData)==2) {
         $result[$lineData[0]] = $lineData[1];
    }
}

The following will work: 以下将有效:

foreach($response as $line) {
    if (strpos($line, "=") === FALSE) { continue; }   // Skip the line if there's no assignment action going on.
    list($var, $value) = explode("=", trim($line));   // Get the parts on either side of the "=".
    $result[$var] = (empty($value) ? NULL : $value);  // Assign the value to the result array, making the value NULL if it's empty.
}

Adding my 2 cents because some of the solutions posted so far are being overly clever, or not 100% correct. 再加上我的2美分,是因为到目前为止发布的某些解决方案过于聪明,或者不是100%正确。

$lines = explode("\n", $paypal_response);
$lines = array_map('trim', $lines);
$status = array_shift($lines);
if ($status != 'SUCCESS') {
    // Transaction was not successful
}
$values = array();
foreach($lines as $line) {
    list($key, $value) = explode('=', $line, 2);
    $values[$key] = $value;
}

Here is a more complex solution: 这是一个更复杂的解决方案:

//$result=the result from paypal
parse_str(str_replace(PHP_EOL,'&',$result),$result);
var_dump($result);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM