Jump to content

Need advice on how best to go through an array and match strings


OM2

Recommended Posts

I've some code for an ecommerce plugin

It outputs (in an email) all fields relating to the order

I don't want all fields - only selected ones

 

I wanted some advice on how to replace the current loop and also how best to compare 2 strings

 

The code I have is:

 

foreach ($p->ipn_data as $key => $value) { $body .= "\n$key: $value"; }

 

This output something like this:

 

shopaction: redirect

shipping_1: 0.00

first_name: Firstname

last_name: Lastname

email: email@yahoo.co.uk

phone: 020

address1: 10 Road

address2:

city: London

state:

zip: EC1V 0HB

country: GB

collection-delivery: collection

shop_payment: cash

item_name_1: Test item 1

eshopident_1: 3205c068dbc16cd829329b6e9917b37ab

quantity_1: 1

weight_1: 0

amount_1: 3.50

item_number_1: test-item-1

postid_1: 668

numberofproducts: 1

amount: 3.50

item_name_2: Test item 2

eshopident_2: 3205c068dbc16cd829329b6e9917b37ab

quantity_2: 1

weight_2: 0

amount_2: 3.50

item_number_2: test-item-2

postid_2: 669

numberofproducts: 1

amount: 4.50

submit: Proceed to Confirmation »

custom: 220120112004817

reference:

comments:

RefNr: 32728455914f0e2dd143c06

ppsubmit: Proceed to Checkout »

payer_email: Firstname Lastname email@yahoo.co.uk

 

I'm scratching my head and am not sure where to start

 

One possibility would be to check if my chosen values that I want to output matched $key or not

 

For this, I'd need to figure out to compare strings and get a positive if a subset of the string was contained

I need to do this because of things like 'quantity_1' and 'quantity_2'

I've looked at string comparison functions and can't figure out which to use

 

OK... I think I can do it using my proposed method!

But... the thing is... I would have though that was a lot of waste of CPU uneccesarily

 

If I only wanted 5 fields and there were 10 fields, then that a potentially of 50 comparisons just to check for the $key value?

 

Is there a better way?

 

I'm not sure where to investigate the data - I'm not sure where the numericals get put in for something like 'quantity_1'

I assume it starts life as just 'quantity_'?

 

ANY pointers where to start would be great

I know I could code a half baked solution - but I don't think it would be very optimal!

 

Thanks

 

 

OM

Link to comment
Share on other sites

I would use a preg_replace to remove any _n sequence numbers at the end of the $key value, leaving you with the base field name. You could then use an in_array statement to match only those field names that you want.

ok... that makes sense

but i've been thinking... i need the _n's - these could be any number - and need to be printed out?

 

i was thinking of doing something like this instead:

 

foreach ($p->ipn_data as $key => $value) 
{
if (strpos($key, 'firstname')
{
	$body .= "\n$key: $value"; 
}
else if (strpos($key, 'itemname_')
{
	$body .= "\n$key: $value"; 
}
else if (strpos($key, 'total')
{
	$body .= "\n$key: $value"; 
}
}

(the above is simplified just to explain the point)

 

so the above would be ok if there were itemname_1, itemname_2, itemname_3 ...... itemname_10

there would be any number of these and we'd be ok

 

i can't think of a better solution

 

let me know what u think

 

EDIT: is my use of strpos OK?? i looked up in manual and this seemed to be the only choice

 

THANKS!

Link to comment
Share on other sites

<?php
$keep = array('quantity','amount'); // fields to keep
$body = '';
foreach($p->ipn_data as $key=>$value){
$base_key = preg_replace('/(_\d+)$/','',$key); // get the base key w/o the _nn
if(in_array($base_key,$keep)){ // test if the base key is in the keep array
	$body .= "\n$key: $value"; // add the original key/value to the body
}

}

echo '<pre>',$body,'</pre>'; // show the results for demo purposes

Link to comment
Share on other sites

<?php
$keep = array('quantity','amount'); // fields to keep
$body = '';
foreach($p->ipn_data as $key=>$value){
$base_key = preg_replace('/(_\d+)$/','',$key); // get the base key w/o the _nn
if(in_array($base_key,$keep)){ // test if the base key is in the keep array
	$body .= "\n$key: $value"; // add the original key/value to the body
}

}

echo '<pre>',$body,'</pre>'; // show the results for demo purposes

OMG - that looks sooo much better than my code    :)

in effect, is it doing what i proposed exactly, but in less lines of code? or is it a more optimal way?

 

ALSO: is using preg_replace better than using the strpos is used?

 

thanks a million

 

ASIDE: can u recommend somewhere to learn regular expressions. i'll take ur word it that what uve done inside the pregrepalce works! i've seen some big fat books on regular expressions - which i don't fancy reading. a 4 page dummies guide would do me fine to get me started. thanks

Link to comment
Share on other sites

ok, the code works a real treat. THANK YOU!

 

the output i now get is:

 

shipping_1: 0.00

first_name: Joe

last_name: Bloggs

email: email@yahoo.co.uk

phone: 020

address1: 10 Road

address2:

city: London

zip: EC1V 0HB

collection-delivery: collection

eshop_payment: cash

item_name_1: item one

quantity_1: 1

amount_1: 12.95

item_name_2: item number two

quantity_2: 1

amount_2: 5.95

item_name_3: item num three

quantity_3: 1

amount_3: 5.50

numberofproducts: 3

amount: 24.40

 

what i actually would prefer is something like this:

 

 

shipping_1: 0.00

name: Joe Bloggs

email: email@yahoo.co.uk

phone: 020

address1: 10 Road, London, EC1V 0HB

collection-delivery: collection

eshop_payment: cash

 

order:

 

1 x item one 12.95

1 x item number two 5.95

1 x item num three 5.50

 

amount: 24.40

 

much more readable that way

 

what would be the best method of doing this?

may parse twice? first time getting the other data and second time getting the item specific data (for which there could be any number)

 

i'm thinking if i would be better to do in one loop?

i think that would involve keeping track of 3 strings - the beginning, the middle part (where u have all the item_1, item_2, item_3 etc) and the end part

 

the bit i'm not 100% sure about is how best to tie up item_1, quantity_1 and price_1 for example

 

any ideas?

 

thanks

 

Link to comment
Share on other sites

I would use a template -

 

<?php

// Note: the index values in this array have significance. Index values 0-11 correspond to main template tags. Index values 12-14 correspond to repeating order template tags.
$keep = array(
'shipping_','first_name','last_name','email','phone','address1','address2','city','zip','collection-delivery','eshop_payment','amount',
'item_name_','quantity_','amount_');

$main_template = "shipping_1: {shipping_}
name: {first_name} {last_name}
email: {email}
phone: {phone}
address1: {address1}, {city}, {zip}
collection-delivery: {collection-delivery}
eshop_payment: {eshop_payment}

order:

{order}
amount: {amount}
";

$each_order_template = "{quantity_} x {item_name_} {amount_}
";

foreach($p->ipn_data as $key=>$value){
$base_key = preg_replace('/(_\d+)$/','_',$key);
if(in_array($base_key,$keep)){
	// data (assoc) key found
	$pos = array_search($base_key, $keep); // get corresponding index (index 0-11 are normal items, 12-14 are repeating items)
	if($pos < 12){
		// normal template item
		$tpl[$base_key] = $value;
	} else {
		// order/repeating template item
		$otpl[$base_key][] = $value;
	}
}
}

// exchange (pivot) the order data rows and columns
foreach($otpl as $key => $arr){
foreach($arr as $num=>$value){
	$pivot[$num][$key] = $value;
}
}

$tpl['order'] = ''; // build/populate the repeating order template
foreach($pivot as $row){
$tpl['order'] .= preg_replace("/\{([^\{]{1,100}?)\}/e","\$row['$1']",$each_order_template); // populate template
}

// build/populate the main template
$body = preg_replace("/\{([^\{]{1,100}?)\}/e","\$tpl['$1']",$main_template); // populate template

echo "<pre>"; // display literal output for debugging
echo $body;

 

I changed the preg_replace slightly so that the trailing under_score _ is kept so that the amount value can be distinguished from the amount_n values.

Link to comment
Share on other sites

OMG

erm... i don't know what to say

how long did that take u to write???

probably took you 5 minutes

would take ma whole day    :)

but then again... i've never used templates as u describe

 

i'm going to have to spend time going through the code now - i'll post back here after

 

just glancing at the code... i'm not sure about the bit where  u mention about 1-11 and 12-14?

i'll ask again if the code really confuses me

Link to comment
Share on other sites

If I were to write that code today, I would split the $keep array into two arrays and then use two different if(in_array()){} statements, in place of the existing in_array, the array_search, and the if($pos<12){}else{} logic.

i wasnt sure what u were saying here?

is that describing a better way to code it?

let me know

thanks

Link to comment
Share on other sites

New keep arrays -

$keep_main = array(
'shipping_','first_name','last_name','email','phone','address1','address2','city','zip','collection-delivery','eshop_payment','amount');
$keep_order = array('item_name_','quantity_','amount_');

 

New matching logic -

foreach($data as $key=>$value){
$base_key = preg_replace('/(_\d+)$/','_',$key);
if(in_array($base_key,$keep_main)){
	// normal template item
	$tpl[$base_key] = $value;
}
if(in_array($base_key,$keep_order)){
	// order/repeating template item
	$otpl[$base_key][] = $value;
}
}

 

Link to comment
Share on other sites

wow...!

thanks

i need some time to go through the code

 

i've copied and pasted what u've given + added the new additions

but am getting 2 errors for:

 

foreach($otpl as $key => $arr){

 

and

 

foreach($pivot as $row){

 

for both it says "invlaid argument supplied foreach()"

 

any ideas?

 

thanks

 

Link to comment
Share on other sites

I came up with this solution

<?php
  $text=<<<EOF
shipping_1: 0.00
first_name: Joe
last_name: Bloggs
email: email@yahoo.co.uk
phone: 020
address1: 10 Road
address2:
city: London
zip: EC1V 0HB
collection-delivery: collection
eshop_payment: cash
item_name_1: item one
quantity_1: 1
amount_1: 12.95
item_name_2: item number two
quantity_2: 1
amount_2: 5.95
item_name_3: item num three
quantity_3: 1
amount_3: 5.50
numberofproducts: 3
amount: 24.40
EOF;
  $template=<<<EOF
shipping: {shipping:shipping_\d+}
name: {first_name} {last_name}
email: {email}
phone: {phone}
address: {address:address[12]}, {city} {zip}
collection-delivery: {collection-delivery}
eshop_payment: {eshop_payment}

order:
{items:(item_name|quantity|amount)_(\d+)}

amount: {amount}
EOF;
  header('Content-Type: text/plain');
  
function adddlm($pattern)
{
  return "@^($pattern): (.*?)[\\r\\n]*\$@m";
}
function process_address($match)
{
   global $var;
   $var[]=$match[2];
}
function end_address()
{
  global $var;
  return implode(', ',$var);
}
function process_shipping($match)
{
  global $var;
  $var[]=$match[2];
}
function end_shipping()
{
  global $var;
  return number_format(array_sum($var),2);
}
function process_items($match)
{
  global $var;
  $var[$match[3]][$match[2]]=$match[4];
  return $match[0];
}
function do_item($item)
{
  return "$item[quantity] X $item[item_name] $item[amount]";
}
function end_items()
{
  global $var;
  $var=array_map('do_item',$var);
  return implode(PHP_EOL,$var);
}
  preg_match_all('@^([^:]+): (.*?)[\r\n]*$@m',$text,$matches,PREG_SET_ORDER);
  foreach($matches as $match)
    $vars[$match[1]]=$match[2];
  preg_match_all('@{([^}]+)}@',$template,$matches);
  $output=$template;
  foreach($matches[1] as $tplfun)
  {
    if(strpos($tplfun,':'))
    {
      list($func,$patterns)=explode(':',$tplfun);
      $patterns=array_map('adddlm',explode(',',$patterns));
      if(function_exists("process_$func"))
      {
        $var=NULL;
        preg_replace_callback($patterns,"process_$func",$text);
        $var=call_user_func("end_$func");
      } else {
        $var='N/A';
      }
    } else {
      $var=isset($vars[$tplfun])?$vars[$tplfun]:'N/A';
    }
    $output=str_replace('{'.$tplfun.'}',$var,$output);
  }
  echo $output;
?>

 

The only part I didnt implement was the multiple item sets line within the template, It's possible, just have to implement more code in the template structure.

Takes two formats, simple replacement {var} and complex (multiple lines)

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.