I have a query string like the one given below:
http://localhost/project/viewMember.php?sort=Y2xhc3M=&class=Mw==&page=9
Now variable: page in query string can be anywhere within the query string either in beginning or middle or at end (like ?page=9 or &page=9& or &page=9).
Now, I need to remove page=9 from my query string and get a valid query string.
Lots of ways this could be done, including regex (as seen below). This is the most robust method I can think of, although it is more complex than the other methods.
Use parse_url to get the query string from the url (or write your own function).
Use parse_str to convert the query string into an array
unset the key that you don't want
Use http_build_query to reassemble the array into a query string
Then reconstruct the Url (if required)
Try:
preg_replace('/page=\d+/', '', $url);
Tried writing a function for this. Seems to work:
<?php
$url = "http://localhost/project/viewMember.php?sort=Y2xhc3M=&class=Mw==&page=9";
// prints http://localhost/project/viewMember.php?sort=Y2xhc3M=&class=Mw==
print changeURL($url) . "\n";
$url = "http://localhost/project/viewMember.php?sort=Y2xhc3M=&page=9&class=Mw==";
// prints http://localhost/project/viewMember.php?sort=Y2xhc3M=&class=Mw==
print changeURL($url) . "\n";
function changeURL($url)
{
$arr = parse_url($url);
$query = $arr['query'];
$pieces = explode('&',$query);
for($i=0;$i<count($pieces);$i++)
{
if(preg_match('/^page=\d+/',$pieces[$i]))
unset($pieces[$i]);
}
$query = implode('&',$pieces);
return "$arr[scheme]://$arr[host]$arr[user]$arr[pass]$arr[path]?$query$arr[fragment]";
}
?>
I created these two functions:
function cleanQuery($queryLabels){
// Filter all items in $_GET which are not in $queryLabels
if(!is_array($queryLabels)) return;
foreach($_GET as $queryLabel => $queryValue)
if(!in_array($queryLabel, $queryLabels) || ($queryValue == ''))
unset($_GET[$queryLabel]);
ksort($_GET);
}
function amendQuery($queryItems = array()){
$queryItems = array_merge($_GET, $queryItems);
ksort($queryItems);
return http_build_query($queryItems);
}
To remove the page part I would use
$_GET = amendQuery(array('page'=>null));
cleanQuery does the opposite. Pass in an array of the terms you want to keep.
function remove_part_of_qs($removeMe)
{
$qs = array();
foreach($_GET as $key => $value)
{
if($key != $removeMe)
{
$qs[$key] = $value;
}
}
return "?" . http_build_query($qs);
}
echo remove_part_of_qs("page");
This should do it, this is my first post on StackOverflow, so go easy!
Related
I need to join/concatenate strings from a return of a function call, and from a variable without using the traditional concatenation ..
In this scenario, it should be displaying a url string.
Below is my actual code.
CODE:
$test = "{ config('app.url') }/{ $username }";
die(print_r($test));
Current Result:
{ config('app.url') }/testuser
Expected Result:
http://localhost:8000/testuser
You may read more about complex (curly) syntax in a quoted string, however you may achieve what you want with that code:
$test = "{${config('app.url')}}/{$username}";
die(print_r($test));
But I personally prefer:
$appUrl = config('app.url');
$test = "{$appUrl}/{$username}";
die(print_r($test));
Does that work for you?
It's not possible. You can only parse variables inside string. The only way is assigning function result to variable:
$url = config('app.url');
$test = "{$url}/{$username}";
You can read more about Variable parsing in strings
You can try the following way
<?php
$_ = function ( $v ) { return $v; };
function config($url)
{
return $url;
}
$username = 'u_name';
echo "{$_( config('app.url') )}/{$username}";
I want to extract all used parameters of a link as a text string. Example:
$link2 = http://example.com/index.html?song=abcdefg;
When using the above link $param should give out all the parameters '?song=abcdefg'. Unfortunately I do not know the id index.html nor the parameters and their respective data values.
As much as I am informed there is the function $_GET, which creates an array, but I need a string.
You can use parse_url:
$link2 = 'http://example.com/index.html?song=abcdefg';
$param = '?' . parse_url($link2, PHP_URL_QUERY);
echo $param;
// ?song=abcdefg
Many librairies exist to parse url, you can use this one for an exemple :
https://github.com/thephpleague/uri
use League\Uri\Schemes\Http as HttpUri;
$link2 = 'http://example.com/index.html?song=abcdefg';
$uri = HttpUri::createFromString($link2);
// then you can access the query
$query = $uri->query;
You also can try this one :
https://github.com/jwage/purl
A weird way to do this is
$link2 = 'http://example.com/index.html?song=abcdefg';
$param = strstr($link2, "?");
echo $param // ?song=abcdefg
strstr($link2, "?") will get everything after the first position of ?; including the leading ?
you can loop over the get array and parse it into a string:
$str = "?"
foreach ($_GET as $key => $value) {
$temp = $key . "=". $value . "&";
$str .= $temp
}
rtrim($str, "&")//remove leading '&'
You can use http_build_query() method
if ( isset ($_GET))
{
$params = http_build_query($_GET);
}
// echo $params should return "song=abcdefg";
Is there a shortcut method to assigning $_GET['values'] to variables?
I currently do like others do:
if(isset($_GET['type'],$_GET['case'])
$type = $_GET['type'];
$case = $_GET['case'];
Is there a cleaner method to do this instead of doing like below separately.
$type = $_GET['type'];
$case = $_GET['case'];
http://docs.php.net/extract
I think you're looking for extract function.
extract($_GET); //now, all of the functions are in current symbol table
Well, with array map you can you get the case not just once, but all at once, and you can also check for isset() and empty() at the same time too.
Suppose, you have this URL: read.php?id=1&name=foo&job=student&country=Brazil
Your problem is fetching the $_GET type, and you may need to check if is it empty/isset or not right?
Well, first you create a function to iterate through it.
function checkGet($val){
return (isset($val) && !empty($val)) ? $val : null;
}
Then, you callback that function with array_map()
$check = array_map('checkGet', $_GET);
And that is it!
If you were to do var_dump($check); now, you would get get all the types, and values:
array (size=4)
'id' => string '1' (length=1)
'name' => string 'foo' (length=3)
'job' => string 'student' (length=7)
'country' => string 'Brazil' (length=6)
Meaning, after this, instad of doing:
if(isset($_GET['something']) && !empty($_GET['something']))
$var = $_GET['something'];
echo $var;
Just do:
echo $check['something']
The only one-line code I can think of, to make sure that you still do the necessary checks, is
$type = (isset($_GET['type'])) ? $_GET['type'] : 'a default value or false';
Reading comments, I understand you may want to do this:
foreach($_GET as $key=>$value) {
$$key = $value;
}
I would suggest though, to always initialize the variables you need only. The above code will result in getting unknown variables, which may actually give the user a way to manipulate your script.
Example:
index.php?ExpectedVar=1&UserDefinedVar=2
will generate the following variables in your code:
$ExpectedVar // 1 <- you wanted this one
$UserDefinedVar // 2 <- but what about this?
What if you had this script called by some other script?
Then even if you have this code at the top of your file, you may have some variables overwritten from a user defined $_GET!
Disaster case Scenario:
script1.php
<?php
$tableToDelete = "old_products";
include("script2.php");
?>
script2.php
<?php
foreach($_GET as $key=>$value) {
$$key = $value;
}
// user added &tableToDelete=users
// DROP TABLE $table
// will gloriously delete users
?>
Instead by writing a few lines with the original code I posted, you can get the variables you need at the start of your php script and use them with a clear mind.
Try like
foreach($_GET as $key=>$value) {
$get_arr[$key] = $_GET[$key];
}
print_r($get_arr);
I would do it that way, this way you make sure that it will only return TRUE or FALSE
if (!isset($_GET['type']) || empty($_GET['type'])) {
// Display error
} else {
$type = $_GET['type'];
$case = $_GET['case'];
}
Or you can do it that way as well
$type = (isset($_GET['type'])===false)?'':trim($_GET['type']);
$case = (isset($_GET['case'])===false)?'':trim($_GET['case']);
$_GET is table, so you can easy use foreach function
For example
foreach ($_GET as $key => $value) {
... = $value;
}
If you would like to create variables with $key names use variable variables
PHP Manual Variable Variables
You can do it through extract()
extract($_GET, EXTR_PREFIX_ALL, 'g');
so that
$_GET['val'] becomes $g_val
Note the third parameter: g it prepends g_ to the keys.
This (untested) class should help you:
class MyGet {
public static $myValues = array();
public static function setMyValues($keywords, $where) {
MyGet::$myValues = array();
for ($index = 0; $index < count($keywords); $index++) {
if ((!(isset($where[$keywords[$index]]))) || (empty($where[$keywords[$index]]))) {
MyGet::$myValues = array();
return false;
}
MyGet::$myValues[$keywords[$index]] = $where[$keywords[$index]];
}
}
}
You can use it like this:
if (MyGet::setMyValues(array(0 => "type", 1 => "case"), $_GET)) {
//the values are initialized
} else {
//the values are not initialized
}
From what I understand youtube.com uses three types of urls for their video links.
http://www.youtube.com/watch?v=8uLPtmCroQ8&feature=related
http://www.youtube.com/watch?v=8uLPtmCroQ8
http://youtu.be/8uLPtmCroQ8
I get this url submitted to my site in any one of these different ways and I store the url into a custom field called $video_code. I need to strip it of any parameters that come after the id of the video so if a user submit the first url above, &feature=related gets stripped. I'm using php.
If I understand your problem correctly, You could use something like this to store the video id in the databse and then construct the url as you like.
function getVideoId($url)
{
$parsedUrl = parse_url($url);
if ($parsedUrl === false)
return false;
if (!empty($parsedUrl['query']))
{
$query = array();
parse_str($parsedUrl['query'], $query);
if (!empty($query['v']))
return $query['v'];
}
if (in_array(strtolower($parsedUrl['host']), array('youtu.be', 'www.youtu.be')))
return trim($parsedUrl['path'], '/');
return false;
}
$input = array('http://www.youtube.com/watch?v=8uLPtmCroQ8&feature=related', 'http://www.youtube.com/watch?v=8uLPtmCroQ8', 'http://youtu.be/8uLPtmCroQ8');
foreach ($input as $url)
{
echo getVideoId($url) . PHP_EOL;
}
In which language did you want to this? If it is in PHP you should look at this.
You could also do a regular expressions to split the string. Take a look here: http://www.php.net/manual/en/function.preg-split.php
Use this code:
$arr=array(
'http://www.youtube.com/watch?v=8uLPtmCroQ8&feature=related',
'http://www.youtube.com/watch?v=8uLPtmCroQ8',
'http://youtu.be/8uLPtmCroQ8',
);
for($i=0; $i<count($arr); $i++){
$urlarr = parse_url($arr[$i]);
if (!empty($urlarr['query'])) {
parse_str($urlarr['query']);
$qarr = array();
if (!empty($v))
$qarr['v'] = $v;
$urlarr['query'] = http_build_query($qarr);
$arr[$i] = http_build_url('', $urlarr);
}
}
print_r($arr);
OUTPUT:
Array
(
[0] => http://www.youtube.com/watch?v=8uLPtmCroQ8
[1] => http://www.youtube.com/watch?v=8uLPtmCroQ8
[2] => http://youtu.be/8uLPtmCroQ8
)
function getVideoCode($url){
$videoCode;
$code_parse = parse_url($url);
if(empty($code_parse["query"])){
$videoCode = str_replace("/"," ",$code_parse["path"]);
}else{
$videoCode = clearQuery($code_parse["query"]);
}
echo $videoCode;
}
function clearQuery($query){
$redundant = array("v", "&", "feature","=","related");
return str_replace($redundant," ",$query);
}
It is not a professional code but It's easy to understand.When I call like this:
getVideoCode("http://youtu.be/8uLPtmCroQ8");
getVideoCode("http://www.youtube.com/watch?v=8uLPtmCroQ8");
getVideoCode("http://www.youtube.com/watch?v=8uLPtmCroQ8&feature=related");
The Output is
8uLPtmCroQ8
8uLPtmCroQ8
8uLPtmCroQ8
I've the following method which allows me to protect MySQL entities:
public function Tick($string)
{
$string = explode('.', str_replace('`', '', $string));
foreach ($string as $key => $value)
{
if ($value != '*')
{
$string[$key] = '`' . trim($value) . '`';
}
}
return implode('.', $string);
}
This works fairly well for the use that I make of it.
It protects database, table, field names and even the * operator, however now I also want it to protect function calls, ie:
AVG(database.employees.salary)
Should become:
AVG(`database`.`employees`.`salary`) and not `AVG(database`.`employees`.`salary)`
How should I go about this? Should I use regular expressions?
Also, how can I support more advanced stuff, from:
MAX(AVG(database.table.field1), MAX(database.table.field2))
To:
MAX(AVG(`database`.`table`.`field1`), MAX(`database`.`table`.`field2`))
Please keep in mind that I want to keep this method as simple/fast as possible, since it pretty much iterates over all the entity names in my database.
If this is quoting parts of an SQL statement, and they have only complexity that you descibe, a RegEx is a great approach. On the other hand, if you need to do this to full SQL statements, or simply more complicated components of statements (such as "MAX(AVG(val),MAX(val2))"), you will need to tokenize or parse the string and have a more sophisticated understanding of it to do this quoting accurately.
Given the regular expression approach, you may find it easier to break the function name out as one step, and then use your current code to quote the database/table/column names. This can be done in one RE, but it will be tricker to get right.
Either way, I'd highly recommend writing a few unit test cases. In fact, this is an ideal situation for this approach: it's easy to write the tests, you have some existing cases that work (which you don't want to break), and you have just one more case to add.
Your test can start as simply as:
assert '`ticked`' == Tick('ticked');
assert '`table`.`ticked`' == Tick('table.ticked');
assert 'db`.`table`.`ticked`' == Tick('db.table.ticked');
And then add:
assert 'FN(`ticked`)' == Tick('FN(ticked)');
etc.
Using the test case ndp gave I created a regex to do the hard work for you. The following regex will replace all word boundaries around words that are not followed by an opening parenthesis.
\b(\w+)\b(?!\()
The Tick() functionality would then be implemented in PHP as follows:
function Tick($string)
{
return preg_replace( '/\b(\w+)\b(?!\()/', '`\1`', $string );
}
It's generally a bad idea to pass the whole SQL to the function. That way, you'll always find a case when it doesn't work, unless you fully parse the SQL syntax.
Put the ticks to the names on some previous abstraction level, which makes up the SQL.
Before you explode your string on periods, check if the last character is a parenthesis. If so, this call is a function.
<?php
$string = str_replace('`', '', $string)
$function = "";
if (substr($string,-1) == ")") {
// Strip off function call first
$opening = strpos($string, "(");
$function = substr($string, 0, $opening+1);
$string = substr($string, $opening+1, -1);
}
// Do your existing parsing to $string
if ($function == "") {
// Put function back on string
$string = $function . $string . ")";
}
?>
If you need to cover more advanced situations, like using nested functions, or multiple functions in sequence in one "$string" variable, this would become a much more advanced function, and you'd best ask yourself why these elements aren't being properly ticked in the first place, and not need any further parsing.
EDIT: Updating for nested functions, as per original post edit
To have the above function deal with multiple nested functions, you likely need something that will 'unwrap' your nested functions. I haven't tested this, but the following function might get you on the right track.
<?php
function unwrap($str) {
$pos = strpos($str, "(");
if ($pos === false) return $str; // There's no function call here
$last_close = 0;
$cur_offset = 0; // Start at the beginning
while ($cur_offset <= strlen($str)) {
$first_close = strpos($str, ")", $offset); // Find first deep function
$pos = strrpos($str, "(", $first_close-1); // Find associated opening
if ($pos > $last_close) {
// This function is entirely after the previous function
$ticked = Tick(substr($str, $pos+1, $first_close-$pos)); // Tick the string inside
$str = substr($str, 0, $pos)."{".$ticked."}".substr($str,$first_close); // Replace parenthesis by curly braces temporarily
$first_close += strlen($ticked)-($first_close-$pos); // Shift parenthesis location due to new ticks being added
} else {
// This function wraps other functions; don't tick it
$str = substr($str, 0, $pos)."{".substr($str,$pos+1, $first_close-$pos)."}".substr($str,$first_close);
}
$last_close = $first_close;
$offset = $first_close+1;
}
// Replace the curly braces with parenthesis again
$str = str_replace(array("{","}"), array("(",")"), $str);
}
If you are adding the function calls in your code, as opposed to passing them in through a string-only interface, you can replace the string parsing with type checking:
function Tick($value) {
if (is_object($value)) {
$result = $value->value;
} else {
$result = '`'.str_replace(array('`', '.'), array('', '`.`'), $value).'`';
}
return $result;
}
class SqlFunction {
var $value;
function SqlFunction($function, $params) {
$sane = implode(', ', array_map('Tick', $params));
$this->value = "$function($sane)";
}
}
function Maximum($column) {
return new SqlFunction('MAX', array($column));
}
function Avg($column) {
return new SqlFunction('AVG', array($column));
}
function Greatest() {
$params = func_get_args();
return new SqlFunction('GREATEST', $params);
}
$cases = array(
"'simple'" => Tick('simple'),
"'table.field'" => Tick('table.field'),
"'table.*'" => Tick('table.*'),
"'evil`hack'" => Tick('evil`hack'),
"Avg('database.table.field')" => Tick(Avg('database.table.field')),
"Greatest(Avg('table.field1'), Maximum('table.field2'))" => Tick(Greatest(Avg('table.field1'), Maximum('table.field2'))),
);
echo "<table>";
foreach ($cases as $case => $result) {
echo "<tr><td>$case</td><td>$result</td></tr>";
}
echo "</table>";
This avoids any possible SQL injection while remaining legible to future readers of your code.
You could use preg_replace_callback() in conjunction with your Tick() method to skip at least one level of parens:
public function tick($str)
{
return preg_replace_callback('/[^()]*/', array($this, '_tick_replace_callback'), $str);
}
protected function _tick_replace_callback($str) {
$string = explode('.', str_replace('`', '', $string));
foreach ($string as $key => $value)
{
if ($value != '*')
{
$string[$key] = '`' . trim($value) . '`';
}
}
return implode('.', $string);
}
Are you generating the SQL Query or is it being passed to you? If you generating the query I wouldn't pass the whole query string just the parms/values you want to wrap in the backticks or what ever else you need.
EXAMPLE:
function addTick($var) {
return '`' . $var . '`';
}
$condition = addTick($condition);
$SQL = 'SELECT' . $what . '
FROM ' . $table . '
WHERE ' . $condition . ' = ' . $constraint;
This is just a mock but you get the idea that you can pass or loop through your code and build the query string rather than parsing the query string and adding your backticks.