I want to take a url that does not have any apostrophes, commas or ampersands in it and match it with a record in a database that may have one of those characters.
For example:
mywebsite.com/bobs-big-boy
mywebsite.com/tom--jerry
mywebsite.com/one-two-three
rewrite to
index.php?name=bobs-big-boy
index.php?name=tom--jerry
index.php?name=bobs-big-boy
Then in php I want to use the $_GET['name'] to match the records
bob's big boy
tom & jerry
one, two, three
Now my query looks like this:
"SELECT * from the_records WHERE name=$NAME";
I can't change the records, because they're business names. Is there a way I can write the query to ignore ampersands, commas and apostrophes in the db?
Yes you can but I'm pretty sure it will ignore any indexes you have on the column. And it's disgusting.
Something like
SELECT * FROM the_records
WHERE replace(replace(replace(name, '''', ''), ',', ''), '&', '') = $NAME
By the way taking a get variable like that and injecting it into the mysql query can be ripe for sql injection as far as I know.
pg, I know you said you can't change/update the content in the database you're selecting from, but does anything preclude you from making a table in another database you do have write access to? You could just make a map of urlnames to business names and it'd only be slow the first time you do the replace method.
Greetings,
This one took me a few minutes to puzzle out! There are actually a few specifics missing on you requirements, so I've tried to work through the problem with different assumptions, as stated below.
Here is the set of assumed input from the URL, as pulled from your example, along with a MySQL injection attack (just for giggles), and variations on the business names. The keys are the expected URLs and the values are the database values to match.
<?php
$names = array(
'bobs-big-boy'=>"bob's big boy",
'tom--jerry'=>'tom & jerry',
'tomjerry'=>'tom&jerry',
'one-two-three'=>'one, two, three',
'onetwothree'=>'one,two,three',
"anything' OR 'haxor'='haxor"=>'die-haxor-die',
);
?>
One clever way to do an end-run mySQL's lack of regex replacement is to use SOUNDEX, and this approach would seem to mostly work in this case depending on the level of accuracy you need, the density of and similarity of customer names, etc. For example, this generates the soundex values for the values above:
$soundex_test = $names;
$select = 'SELECT ';
foreach ($soundex_test as $name=>$dbname) {
echo '<p>'.$name.': '.soundex($name).' :: '.$dbname.': '.soundex($dbname).'</p>';
$select .= sprintf("SOUNDEX('%s'),", $name);
}
echo '<pre>MySQL queries with attack -- '.print_r($select,1).'</pre>';
So, assuming that there are not customers named 'one, two, three' and separate one named 'onetwothree', this approach should work nicely.
To use this method, your queries would look something like this:
$soundex_unclean = $names;
foreach ($soundex_unclean as $name=>$dbname) {
$soundex_unclean[$name] = sprintf("SELECT * from the_records WHERE name SOUNDS LIKE '%s';", $name).' /* matches name field = ['.$dbname.'] */';
}
echo '<pre>MySQL queries with attack -- '.print_r(array_values($soundex_unclean),1).'</pre>';
However, here is a run that DOES deal with the injection attack (note the new line). I know this isn't the focus of the question, but ajreal mentioned the issue, so I thought to deal with it as well:
$soundex_clean = $names;
foreach ($soundex_clean as $name=>$dbname) {
// strip out everything but alpha-numerics and dashes
$clean_name = preg_replace('/[^[:alnum:]-]/', '', $name);
$soundex_unclean[$name] = sprintf("SELECT * from the_records WHERE name SOUNDS LIKE '%s';", $clean_name).' /* matches name field = ['.$dbname.'] */';
}
echo '<pre>MySQL queries with attack cleaned -- '.print_r($soundex_unclean,1).'</pre>';
If this approach does not suit, and you decided that the inline replacement approach is sufficient, then do remember to add a replacement for comma to the mix as well. As an example of that approach, I'm assuming here that the single quote, double quote, ampersand, and comma (i.e. ', ", &, and ,) are the only four special characters are included in the database but deleted from the URL, and that any other non-alpha-numeric character, spaces included, are converted to a dash (i.e. -).
First, a run that does not deal with the injection attack:
$unclean = $names;
foreach ($unclean as $name=>$dbname) {
$regex_name = preg_replace('/[-]+/', '[^[:alnum:]]+', $name);
$unclean[$name] = sprintf("SELECT * from the_records WHERE REPLACE(REPLACE(REPLACE(REPLACE(name, ',', ''), '&', ''), '\"', ''), \"'\", '') REGEXP '%s'", $regex_name);
}
echo '<pre>MySQL queries with attack -- '.print_r($unclean,1).'</pre>';
Second, a run that DOES deal with the attack:
$clean = $names;
foreach ($clean as $name=>$dbname) {
$regex_name = preg_replace('/[^[:alnum:]-]/', '', $name);
$regex_name = preg_replace('/[-]+/', '[^[:alnum:]]+', $regex_name);
$clean[$name] = sprintf("SELECT * from the_records WHERE REPLACE(REPLACE(REPLACE(REPLACE(name, ',', ''), '&', ''), '\"', ''), \"'\", '') REGEXP '%s'", $regex_name);
}
echo '<pre>MySQL queries with attack cleaned -- '.print_r($clean,1).'</pre>';
Aaaand that's enough brainstorming for me for one night! =o)
Using str_replace function we will grab the $name parameter and replace
ampersands (&) with ""
spaces (" ") with "-"
commas (",") with ""
apostrophes("'") with ""
str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
$Search = { "&", " ", ",", "'" }
$Replace = { "", "-", "", "" }
$ComparableString = str_replace($Search, $Replace, $_GET['name'])
After that we can do the sql query:
$name = mysql_real_escape_string($name, $db_resource);
SELECT * from the_records WHERE name='$name'
It's a little janky, but you could explode the GET and build a WHERE on multiple conditions.
Something like (untested):
$name_array = explode("-", $_GET['name']);
$sql_str = "SELECT * FROM the_records WHERE ";
$first_time = true;
foreach($name_array as $name){
if ($name != ""){
if ($first_time){
$sql_str .= "name LIKE \"%".$name."%\"";
$first_time = false;
}
else {
$sql_str .= " AND name LIKE \"%".$name."%\"";
}
}
}
Related
I have a very long list of names and I am using preg_replace to match if a name from the list is anywhere in the string. If I test it with few names in the regex it works fine, but having in mind that I have over 5000 names it gives me the error "preg_replace(): Compilation failed: regular expression is too large".
Somehow I cannot figure out how to split the regex into pieces so it becomes smaller (if even possible).
The list with names is created dynamically from a database. Here is my code.
$query_gdpr_names = "select name FROM gdpr_names";
$result_gdpr_names = mysqli_query($connect, $query_gdpr_names);
while ($row_gdpr_names = mysqli_fetch_assoc($result_gdpr_names))
{
$AllNames .= '"/'.$row_gdpr_names['name'].'\b/ui",';
}
$AllNames = rtrim($AllNames, ',');
$AllNames = "[$AllNames]";
$search = preg_replace($AllNames, '****', $search);
The created $AllNames str looks like this (in the example 3 names only)
$AllNames = ["/Lola/ui", "/Monica\b/ui", "/Chris\b/ui"];
And the test string
$search = "I am Lola and my friend name is Chris";
Any help is very appreciated.
Since it appears that you can't easily handle the replacement from PHP using a single regex alternation, one alternative would be to just iterate each name in the result set one by one and make a replacement:
while ($row_gdpr_names = mysqli_fetch_assoc($result_gdpr_names)) {
$name = $row_gdpr_names['name'];
$regex = "/\b" . $name . "\b/ui";
$search = preg_replace($regex, '----', $search);
}
$search = preg_replace("/----/", '****', $search);
This is not the most efficient pattern for doing this. Perhaps there is some way you can limit your result set to avoid a too long single alternation.
Ok, I was debugging a lot. Even isolating everything else but this part of code
$search = "Lola and Chris";
$query_gdpr_names = "select * FROM gdpr_names";
$result_gdpr_names = mysqli_query($connect, $query_gdpr_names);
while ($row_gdpr_names = mysqli_fetch_assoc($result_gdpr_names)) {
$name = $row_gdpr_names['name'];
$regex = "/\b" . $name . "\b/ui";
$search = preg_replace($regex, '****', $search);
}
echo $search;
Still, print inside but not outside the loop.
The problem actually was in the database records. There was a slash in one of the records
My question must be simple but I can't figure how to do that:
$input = "Hello, Beautiful, World";
and
$expected_output = "'Hello','Beautiful','World'";
I know I can split text by explode(" ", $input);
but how to join with ', ?
Why I need it?
I need to have it to prepare MySQL query like
SELECT value FROM tab_settings WHERE name IN ('Hello', 'Beautiful', 'World')
from $input
You can use below snippet for the same
echo "'".implode("','",explode(", ", $input))."'";
Demo
You can use REGEXP
SELECT * FROM author WHERE aut_name REGEXP 'hello|benny|meny';
Read here Logical AND operator in mySql REGEXP?
This probably answers your specific question:
<?php
$subject = "Hello, Beautiful, World";
preg_match_all('/(\w+)/', $subject, $words);
$words = $words[1];
$tokens = [];
array_walk($words, function($word) use (&$tokens) {
$tokens[] = "'" . $word . "'";
});
$tokens = implode(',', $tokens);
var_dump($tokens);
The output obviously is:
string(27) "'Hello','Beautiful','World'"
But please allow us to offer a hint here:
The strategy you follow to construct sql queries in such a string based manner is ultimately a very bad idea, since it makes your code vulnerable to what is usually referred to as "sql injection attacks". You want to prevent that.
Please start reading about the advantages of using the combination of "prepared statements" and "parameter binding" to prevent such vulnerabilities.
I got some issues trying to INSERT some data from a php document till i got 2 values which contains quotes inside like :
"Rempli d'étoiles"
i d like to remove the ' by a space or even nothing.
-> "Rempli d etoiles"
Here is my what i tried :
$posdeapostrophe = strpos($array, '\'');
if ($posdeapostrophe == false)
{
...
}
else
{
// it goes in this block when it detects a ', but seems like trim doesnt work as i would
$newchaine = trim($array, '\'');
$sql .= "INSERT INTO categorie (id_cat,name_cat) VALUES (" . $cpt . ",'" .$newchaine . "'); ";
thanks!
You can use str_replace().
$array = "Some string's";
$posdeapostrophe = strpos($array, "'");
$val = '';
if ($posdeapostrophe !== false)
{
$val = str_replace("'", "\'", $array);
}
echo $val;
Also can use instead of strpos() and replace() to escape single quotes.
mysqli_real_escape_string($con, $array ); //for mysqli
mysql_real_escape_string($array , $con); //for mysql
What you are currently doing is quite dangerous.
First of all, you should really use the current recommended method for executing queries which is by using PDO: http://php.net/manual/en/book.pdo.php
This will both solve the quotes problem and a massive security hole (SQLi vulnerability) you have currently introduced in your code.
If you still want to replace the single quotes in your text you can indeed do what #scrowler suggested which is:
$your_string = str_replace("'", "", $your_string);
But please use PDO when interacting with a database since this is really the only (safe and recommended) way of doing this.
I have a string of characters separated by many hashes (#). I need to get the individual words in between the hashes on php. here's what my code looks like:
$sql = "SELECT attribute_type.at_name,attribute_type.at_id FROM attribute_type
WHERE attribute_type.prodType_id = $pt_id
AND attribute_type.at_id NOT IN (SELECT at_id
FROM attribute_type
WHERE attribute_type.at_name = 'Product')";
while($items. strpos("#")>0){
// add the selected AT in every loop to be excluded
// .
// here tokens from $items are stored individually in
// $selectedAT (whose value changes in every loop/cycle)
//
// add to the current sql statement the values $at_id and $searchparam
$sql = $sql . "AND attribute_type.at_id NOT IN
(SELECT at_id FROM attribute_type
WHERE attribute_type.at_name = '$selectedAT')";
}
$dbcon = new DatabaseManager();
$rs = $dbcon->runQuery($sql);
explode creates an array by splitting a string on a given token
$words = explode("#", $items);
Now if you need to take these words you extracted from the string and use them to compare to some column in a SQL query...
$sql = "SELECT ... WHERE column IN ('" . implode("', '", $words) . "')";
You should not need to build a query in a loop as you are doing once you have the words in an array.
Even if you did want to do it that way, you don't want to create a subquery for every word when you could just OR the words together in one subquery.
Try strtok. Example paste:
$string = "This is\tan example\nstring";
$tok = strtok($string, " \n\t");
while ($tok !== false) {
echo "Word=$tok<br />";
$tok = strtok(" \n\t");
}
Do not use split as suggested in another answer (which has now been updated). It uses old POSIX regulat expressions and it's deprecated.
To split a string, use $words = explode('#', $items); which does not use a regular expression but a plain string.
Docref: http://php.net/explode
The search query is:
"product1 prod2 prod4"
I need to make the mysql
SELECT *
FROM tableprod
WHERE (prod LIKE '%product1%'
AND prod LIKE '%prod2%'
AND prod LIKE '%prod4%')
using the mysql_real_escape_string for the input query...
Simple string manipulation:
$terms = explode(' ', $search);
$bits = array();
foreach ($terms as $term) {
$bits[] = "prod LIKE '%".mysql_real_escape_string($term)."%'";
}
$query = "SELECT * FROM tableprod WHERE (".implode(' AND ', $bits).")";
If you can meet the constraints, you migh be better off just using a FULLTEXT index which would save you the trouble of having to split the string, plus you'd get the bonus of being able to use basic boolean operators for the search (and/or/not)
without a loop(i've always tried to avoid loops):
$str = "product1 prod2 prod4";
$sql = "select * from tableprod where (prod like '%" . str_replace( ' ', '%\' AND prod LIKE \'%', $str ) . '%\')';
if it's possible you will have more than one space inbetween items use preg_replace