I have a string passed through a campaign source that looks like this:
/?source=SEARCH%20&utm_source=google&utm_medium=cpc&utm_term=<keyword/>&utm_content={creative}&utm_campaign=<campaign/>&cpao=111&cpca=<campaign/>&cpag=<group/>&kw=<mpl/>
when its present I need to cut this up and pass it through to our form handler so we can track our campaigns. I can check for it, hold its contents in a cookie and pass it throughout our site but i am having and issue using preg_match to cut this up and put it into variables so I can pass their values to the handler. I want the end product to look like:
$utm_source=google;
$utm_medium=cpc;
$utm_term=<keyword/>
there is no set number of characters, it could be Google, Bing etc, so i am trying to use preg_match to get the first part (utm_source) and stop past what I want (&) and so forth but I don't understand preg_match well enough to do this.
PHP should be parsing your query sting for you, into $_GET. Otherwise, PHP knows how to parse query strings. Don't use regular expressions or for this, use parse_str.
Input:
<?php
$str = "/?source=SEARCH%20&utm_source=google&utm_medium=cpc&utm_term=<keyword/>&utm_content={creative}&utm_campaign=<campaign/>&cpao=111&cpca=<campaign/>&cpag=<group/>&kw=<mpl/>";
$ar = array();
parse_str($str, $ar);
print_r($ar);
Output:
Array
(
[/?source] => SEARCH
[utm_source] => google
[utm_medium] => cpc
[utm_term] => <keyword/>
[utm_content] => {creative}
[utm_campaign] => <campaign/>
[cpao] => 111
[cpca] => <campaign/>
[cpag] => <group/>
[kw] => <mpl/>
)
Related
I'm currently rewriting a CSRF Token verification function for a client. They have this odd system where ALL of the session information for a user is saved to an sql database field as a string.
$D = {'email' => 'TheDude#company.com','userid' => 69,'uid' => 69,'scanstatus' => 'S','CSRFToken' => 'tokenGENexample==','_SESSION_ATIME' => 1643289149,'abbr' => 'TheDude','scantype' => 0,'_SESSION_REMOTE_ADDR' => 'IP Address','_SESSION_CTIME' => 1643282918,'scanexpiration' => 1643289447,'_SESSION_ETIME' => 1800,'_SESSION_ID' => 'sessionGENexample','scanmode' => 100};;$D
It is stored exactly how you I pasted it.
Then, this verification function uses str_replace to turn it into a line of code that assigns $D to an array:
$D = array('email' => 'TheDude#company.com','userid' => 69,'uid' => 69,'scanstatus' => 'S','CSRFToken' => 'tokenGENexample==','_SESSION_ATIME' => 1643289149,'abbr' => 'TheDude','scantype' => 0,'_SESSION_REMOTE_ADDR' => 'IP Address','_SESSION_CTIME' => 1643282918,'scanexpiration' => 1643289447,'_SESSION_ETIME' => 1800,'_SESSION_ID' => 'sessionGENexample','scanmode' => 100);
It then runs this in an eval() function, and needless to say, is a security risk. I don't even feel like its wise to hold that much sensitive information in a single field, but I digress.
$D is then passed back to our main CSFR Token function, it checks for the data associated with 'CSRFToken' and goes along its merry way.
My idea to removed the eval() was this, and please let me know if this is a terrible idea:
Take the string, parse it for JUST the tokenGENexample==, assign that string directly to $D, and then just rewrite our main CSRF function to look for a value, rather than the associative array value.
To do so, I was exploring a few sub ideas, and that's where I ran into this weird PHP error that I'm asking about right now. Sorry for taking a while to get to the point, but I needed assistance with the parser too, and figured I'd add context.
I decided to use parse_str() BEFORE str_replace() is called, to see if it would spit out all of the values to an output.
My log spit this out: PHP Notice: Array to string conversion...
Well how the heck is it being read as an array, if its clearly still just a string?
Alright, if it wants to be an array, I figured I'd use array_values() to spit me out my values I need.
Welp, then it spits out this warning PHP Warning: array_values() expects parameter 1 to be array...
Now I'm at a loss. It thinks a string is in array, and an array is a string. I think it has to do with it being an associative array.
I went down this huge rabbit hole, simply because the client has some old unsafe code that would run an eval() function to make $D equal that array.
Kind fellows of Stackoverflow, I'm at a darn loss here. Does PHP have built in functions I can use to JUST get the CSRF token out of this string (or convert it to an array?) and assign it $D without that goofy eval() call?
Why oh why would one serialize with var_export() and then mangle it up a bit? And then back-mangle and use eval() to get the data back? I'd recommend batch-converting the lot into JSON, or serialize(), or any other format that can be unserialized without eval(). If you stored it as JSON, you could also query its values if you're running a modern MySQL version.
Now the serialization you have there isn't too far from valid JSON as it is, and can be regexed to conform... I emphasize, this is not best or even good practice!. However if it's for a one-off job, woe is the things we do to get the job done, and moving onward. Here's a "parser":
$str = <<<'DUH'
{'email' => 'TheDude#company.com','userid' => 69,'uid' => 69,'scanstatus' => 'S','CSRFToken' => 'tokenGENexample==','_SESSION_ATIME' => 1643289149,'abbr' => 'TheDude','scantype' => 0,'_SESSION_REMOTE_ADDR' => 'IP Address','_SESSION_CTIME' => 1643282918,'scanexpiration' => 1643289447,'_SESSION_ETIME' => 1800,'_SESSION_ID' => 'sessionGENexample','scanmode' => 100};;$D
DUH;
// Fix ' -quoted keys and values:
$str = preg_replace("~'([^']+)'~", '"\1"', $str);
// Convert => to :
$str = str_replace(' => ', ':', $str);
// Trim the tail:
$str = preg_replace('~;;.+$~', '', $str);
$data = json_decode($str, true);
That could probably be more elegant. This results in:
array(14) {
["email"] · string(19) "TheDude#company.com"
["userid"] · int(69)
["uid"] · int(69)
["scanstatus"] · string(1) "S"
["CSRFToken"] · string(17) "tokenGENexample=="
["_SESSION_ATIME"] · int(1643289149)
["abbr"] · string(7) "TheDude"
["scantype"] · int(0)
["_SESSION_REMOTE_ADDR"] · string(10) "IP Address"
["_SESSION_CTIME"] · int(1643282918)
["scanexpiration"] · int(1643289447)
["_SESSION_ETIME"] · int(1800)
["_SESSION_ID"] · string(17) "sessionGENexample"
["scanmode"] · int(100)
}
Which is a valid PHP array without resorting to evil(). N.B. This will break if you have escaped \' inside keys/values. Need a negative lookbehind for \ before ' if that's the case. If you have 'abbr' => 'O\'Dude', for example. At any rate, it's a good idea to fetch all of the serialized sessions and test to see whether everything parses gracefully.
Good practice: Don't throw this in as a permanent feature. Use it to convert the serialization into coherent data and store it in a more sensible format.
So I would like to take a string like this,
q=Sugar Beet&qf=vegetables&range=time:[34-40]
and break it up into separate pieces that can be put into an associative array and sent to a Solr Server.
I want it to look like this
['q'] => ['Sugar Beets],
['qf'] => ['vegetables']
After using urlencode I get
q%3DSugar+Beet%26qf%3Dvegetables%26range%3Dtime%3A%5B34-40%5D
Now I was thinking I would make two separate arrays that would use preg_split() and take the information between the & and the = sign or the = and the & sign, but this leaves the problem of the final and first because they do not start with an & or end in an &.
After this, the plan was to take the two array and combine them with array_combine().
So, how could I do a preg_split that addresses the problem of the first and final entry of the string? Is this way of doing it going to be too demanding on the server? Thank you for any help.
PS: I am using Drupal ApacheSolr to do this, which is why I need to split these up. They need to be sent to an object that is going to build q and qf differently for instance.
You don't need a regular expression to parse query strings. PHP already has a built-in function that does exactly this. Use parse_str():
$str = 'q=Sugar Beet&qf=vegetables&range=time:[34-40]';
parse_str($str, $params);
print_r($params);
Produces the output:
Array
(
[q] => Sugar Beet
[qf] => vegetables
[range] => time:[34-40]
)
You could use the parse_url() function/.
also:
parse_str($_SERVER['QUERY_STRING'], $params);
I used regexpal.com to test my regexp against the data Wordpress is trying to compare to and it fails, look at this and tell me if you see the problem?
The regexp
"#^json/(.+?)/?([a-zA-Z0-9]*)?$#"
The content to match
json/trips
These works, the previous one doesn't
json/trips/0
json/trips/13
json/fullticket/9805048001130122361809
If I try all these in regexpal they all work, but in wordpress, only the one that doesn't contain the id of the element I want to fetch fails the others work fine.
Interrestingly enough, the $matches return this:
array
0 => string 'json/trips' (length=10)
1 => string 't' (length=1)
2 => string 'rips' (length=4)
Try this regexp instead :
#^json/([^/]+)/?([a-zA-Z0-9]*)?$#
Output :
Array
(
[0] => json/trips
[1] => trips
[2] =>
)
The answer after tweaking the wordpress rewrite rule a bit more ends up being:
data/([^/]+)(/([a-zA-Z0-9\-]*))?$
Note: i changed json to data in the new scenario so i don't mess up the custom post type rules
I want to create an url out of an array with the help of http_build_query (PHP). This is the Array:
$a = array("skip" => 1, "limit" => 1, "startkey" => '["naturalProduct","Apple"]')
After calling
$s = http_build_query($a);
I get the following string $s:
skip=1&limit=1&startkey=%5B%22naturalProduct%22%2C%22Apple%22%5D
My problem is, that I would need an url like this:
skip=1&limit=1&startkey=["naturalProduct","Apple"]
which means, that I don't want to convert the following symbols: ",[]
I have written a conversion function which I call after the http_build_query:
str_replace(array("%5B", "%22", "%5D", "%2C"), array('[', '"', ']', ','), $uri);
My question now: Is there a better way to reach the expected results?
My question now: Is there a better way to reach the expected results?
Yes, there is something better. http_build_queryDocs by default uses an URL encoding as outlined in RFC 1738. You just want to de-urlencode the string. For that there is a function that does this in your case: urldecodeDocs:
$s = http_build_query($a);
echo urldecode($s);
I hope you are aware that your URL then is no longer a valid URL after you've done that. You already decoded it.
You don't need to decode the special characters - they are automatically decoded when PHP's $_GET superglobal is generated. When I do print_r($_GET) with your generated string, I get this:
Array ( [skip] => 1 [limit] => 1 [startkey] => [\"naturalProduct\",\"Apple\"] )
Which has decoded every character, but hasn't unescaped the double quotes. To unescape them, use stripslashes():
echo stripslashes($_GET['startkey']);
This gives
["naturalProduct","Apple"]
Which you can then parse or use however you wish. A better solution, as ThiefMaster mentions in the comments, is to disabled magic_quotes_gpc in your php.ini; it's deprecated and scheduled for removal completely in PHP6.
In CakePHP putting a querystring in the url doesn't cause it to be automatically parsed and split like it normally is when the controller is directly invoked.
For example:
$this->testAction('/testing/post?company=utCompany', array('return' => 'vars')) ;
will result in:
[url] => /testing/post?company=utCompany
While invoking the url directly via the web browser results in:
[url] => Array
(
[url] => testing/post
[company] => utCompany
)
Without editing the CakePHP source, is there some way to have the querystring split when running unit tests?
I have what is either a hack (i.e. may not work for future CakePHP releases) or an undocumented feature.
If the second testAction parameter includes an named array called 'url' then the values will be placed in the $this->params object in the controller. This gives us the same net result as when the controller is directly invoked.
$data = array ('company' => 'utCompany') ;
$result = $this->testAction('/testing/post', array
(
'return' => 'vars',
'method' => 'get',
'url' => $data)
) ;
I'm satisfied with this method for what I need to do. I'll open the question to the community shortly so that it in the future a better answer can be provided.
None of these answers will woerk in Cake 1.3. You should instead set the following before your testAction call:
$this->__savedGetData['company'] = 'utcompany';
CakePHP does provide some level of url splitting but it only seems to work in the run-time configuration and not the test configuration. I'll contact the CakePHP if this is intentional.
I suggestion for your querystring parser would be to use the PHP function explode.
I believe you can do something like this:
$result = explode ('&', $queryString, -1) ;
which would give you your key-pairs in seperate array slots upon which you can iterate and perform a second explode like so:
$keyPair = explode ('=', $result[n], -1) ;
However, all this being said it would be better to peek under the hood of CakePHP and see what they are doing.
What I typed above won't correctly handle situations where your querystring contains html escaped characters (prefixed with &), nor will it handle hex encoded url strings.
use _GET['parmname'];