How to manipulate text in PHP to have single quote and coma - php

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.

Related

How can I use regex to catch unquoted array indices in PHP code and quote them?

PHP 7.2 upgraded undefined constant errors from a notice to a warning, with advice that in future they will return a full-on error instead.
I am trying to identify a way to fix these via scripting, ideally via a regex that I can run to parse each PHP file on a site, find all offending bits of code, and fix them.
I've found multiple examples of how to fix one variant, but none for another, and it's that one that I'm looking for help with.
Here's an example file:
<?php
$array[foo] = "bar";
// this should become
// $array['foo'] = "bar"
echo "hello, my name is $array[foo] and it's nice to meet you";
// would need to become
// echo "hello, my name is " . $array['foo'] . " and it's nice to meet you";
?>
I've seen a lot of options to identify and change the first type, but none for the second, where the undefined constant is within a string. In that instance the parser would need to:
Replace $array[foo] with $array['foo']
Find the entire variable, end quotes beforehand, put a . either side, and then reopen quotes afterwards
Edit: ideally one regexp would deal with both examples in the sample code in one pass - i.e. add the ticks, and also add the quotes/dots if it identifies it’s within a string.
$array[foo] = "bar";
// this should become
// $array['foo'] = "bar"
Yes, this has always triggered a notice and has always been poor practice.
echo "hello, my name is $array[foo] and it's nice to meet you";
// would need to become
// echo "hello, my name is " . $array['foo'] . " and it's nice to meet you";
No, this style has never triggered a notice and does not now. In fact, it's used as an example in the PHP documentation. PHP is never going to remove the ability to interpolate array variables in strings.
Your first case is easy enough to catch with something like this:
$str = '$array[foo] = "bar";';
echo preg_replace("/(\\$[a-z_][a-z0-9_]*)\\[([a-z][a-z0-9_]*)\\]/", "$1['$2']", $str);
But of course needs to be caught only outside of a string.
As with any complex grammar, regular expressions will never be as reliable as a grammar-specific parser. Since you're parsing PHP code, your most accurate solution will be to use PHP's own token parser.
$php = <<< 'PHP'
<?php
$array[foo] = "bar"; // this line should be the only one altered.
$array['bar'] = "baz";
echo "I'm using \"$array[foo]\" and \"$array[bar]\" in a sentence";
echo 'Now I\'m not using "$array[foo]" and "$array[bar]" in a sentence';
PHP;
$tokens = token_get_all($php);
$in_dq_string = false;
$last_token = null;
$output = "";
foreach ($tokens as $token) {
if ($last_token === "[" && is_array($token) && $token[0] === 319 && !$in_dq_string) {
$output .= "'$token[1]'";
} elseif (is_array($token)) {
$output .= $token[1];
} else {
if ($token === "\"") {
$in_dq_string = !$in_dq_string;
}
$output .= $token;
}
$last_token = $token;
}
echo $output;
Output:
<?php
$array['foo'] = "bar"; // this line should be the only one altered.
$array['bar'] = "baz";
echo "I'm using \"$array[foo]\" and \"$array[bar]\" in a sentence";
echo 'Now I\'m not using "$array[foo]" and "$array[bar]" in a sentence';
This code would need some edge cases accounted for, such as when you are intentionally using a constant as an array index.
This isn't perfect, but it should be safe to run multiple times (example)
$str = 'echo "hello, my name is $array[foo] and it\'s nice to meet you";';
echo preg_replace_callback('/\".*(\$.*\[[^\'].*[^\']\]).*\"/', function($match) {
$search = ['[', ']'];
$replace = ["['", "']"];
$array = '" . ' . str_replace($search, $replace, $match[1]) . ' . "';
return str_replace($match[1], $array, $match[0]);
}, $str);
What the regex does is it limits itself to double quoted strings (\"). Then we look for $var[val], without the ticks '. Once we've captured it, we can run it through a callback that does a two-stage str_replace. The first wraps our matched $var[val] with double quotes and inserts the ticks, while the second inserts it into the whole string, using the regex found match
It won't do some things nicely tho. If you have $array[foo] $array[bar], it will wind up as
" . $array['foo'] . "" . $array['bar'] . "
Not pretty, but still valid code

Removing single quotes in PHP

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.

Negating sentences using POS-tagging

I'm trying to find a way to negate sentences based on POS-tagging. Please consider:
include_once 'class.postagger.php';
function negate($sentence) {
$tagger = new PosTagger('includes/lexicon.txt');
$tags = $tagger->tag($sentence);
foreach ($tags as $t) {
$input[] = trim($t['token']) . "/" . trim($t['tag']) . " ";
}
$sentence = implode(" ", $input);
$postagged = $sentence;
// Concatenate "not" to every JJ, RB or VB
// Todo: ignore negative words (not, never, neither)
$sentence = preg_replace("/(\w+)\/(JJ|MD|RB|VB|VBD|VBN)\b/", "not$1/$2", $sentence);
// Remove all POS tags
$sentence = preg_replace("/\/[A-Z$]+/", "", $sentence);
return "$postagged<br>$sentence";
}
BTW: In this example, I'm using the POS-tagging implementation and lexicon of Ian Barber. An example of this code running would be:
echo negate("I will never go to their place again");
I/NN will/MD never/RB go/VB to/TO their/PRP$ place/NN again/RB
I notwill notnever notgo to their place notagain
As you can see, (and this issue is also commented in the code), negating words themselves are being negated as wel: never becomes notnever, which obviously shouldn't happen. Since my regex skills aren't all that, is there a way to exclude these words from the regex used?
[edit] Also, I would very much welcome other comments / critiques you might have in this negating implementation, since I'm sure it's (still) quite flawed :-)
Give this a try:
$sentence = preg_replace("/(\s)(?:(?!never|neither|not)(\w*))\/(JJ|MD|RB|VB|VBD|VBN)\b/", "$1not$2", $sentence);

Shortcut for: $foo = explode(" ", "bla ble bli"); echo $foo[0]

is there a way to get the n-th element of a splitted string without using a variable?
My PHP code always looks like this:
$foo = explode(" ", "bla ble bli");
echo $foo[0];
Is there a shorter way maybe like in Python?
print "bla ble bli".split(" ")[0]
Thanks in advance.
This is what people should be using instead of explode most of the time:
$foo = strtok("bla ble bli", " ");
It cuts off the first string part until the first " ".
If you can't let go of explode, then the closest idiom to accomplish [0] like in Python is:
$foo = current(explode(...));
If it's not just the first element, then it becomes a tad more cumbersome:
$foo = current(array_slice(explode(...), 2)); // element [2]
(Not really an answer per se -- others did answer pretty well)
This is one of the features that should arrive with one of the next versions of PHP (PHP 5.4, maybe).
For more informations, see Features in PHP trunk: Array dereferencing -- quoting one of the given examples :
<?php
function foo() {
return array(1, 2, 3);
}
echo foo()[2]; // prints 3
?>
try this:
its one line:
<?php
echo (($f=explode(" ", "bla ble bli"))?$f[0]:'');
?>
result here:
http://codepad.org/tnhbpYdd
Why not just do:
function splode($string, $delimiter, $index){
$r = explode($delimiter, $string);
return $r[$index];
}
I use like a hojillion little functions like this.
With only one expression I can think of:
echo list($a) = explode(' ', 'a b c') ? $a : '';
echo list($_, $b) = explode(' ', 'a b c') ? $b : '';
Not as far as I know although you could define a function and use that.
function firstWord($string) {
$foo = explode(" ", $string);
return $string;
}
I don't know of a way to do what you want, even though I've wanted to do the same thing many times before. For that specific case you could do
$bar = substr($foo, 0, strpos($foo, " "));
which stops there being one extra variable, but isn't exactly what you wanted.
The following is probably the cleanest way I can think of doing what OP has requested. It defines a function, but no variables of it's own and it'll get the job done for just about any situation:
function at(&$arr, &$pos) { return $arr[$pos]; }
Example usage:
echo at( explode('|', 'a|b|c|d'), 1 ); // Outputs 'b'
The function is a single line of code and wouldn't be hard to commit to memory. If you're only using it once, you can define it in the local scope of where it'll be used to minimize code clutter.
As a slight added benefit, because the function does no checks on $arr or $pos, it'll throw all the same errors that it would if you tried to access a non-existent index for an array, or will even return the individual characters in a string or items in a key-value paired array.
close. the right track is making a function or method for something that gets repeated.
function extract_word($input, $index) {
$input = explode(' ', $input);
return $input[$index];
}
add a third argument of $separater = ' ' if you may have different word separaters.

Ignoring apostrophes in mysql searches

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."%\"";
}
}
}

Categories