How to pass parameter with preg_replace() with 'e' modifier? - php

I have a question about preg_replace() function. I'm using it with 'e' modifier. Here is code snippet:
$batchId = 2345;
$code = preg_replace("/[A-Za-z]{2,4}[\d\_]{1,5}[\.YRCc]{0,4}[\#\&\#\^]{0,2}/e",
'translate_indicator(\'$0\', {$batchId})', $code);
I want to have access to $batchId variable inside translate_indicator($code, $batch=false) function. The above exapmle, unfortunately, doesn't work correctly: $batch is invisible(var_dump() result is bool(false)) within translate_indicator().
Probably, I have syntax mistakes in replacement code. Or, maybe, it's impossible to pass variables with preg_replace()?
Update for the first two answers.
Thank you for answers, but your advice didn't help. Besides I've already tried double qoutes instead of single qoutes. I've just simplified code to test possibility of passing parameter to the function:
$code = preg_replace("/[A-Za-z]{2,4}[\d\_]{1,5}[\.YRCc]{0,4}[\#\&\#\^]{0,2}/e",
"translate_indicator('$0', 12)", $code);
Also I've removed default value for the $batch within translate_indicator(). Result:
Warning: Missing argument 2 for translate_indicator()
So I think it's impossible to pass parameter using this approach.:(

$batchId = 2345;
$code = 'AA1#';
$code = preg_replace(
"/[A-Za-z]{2,4}[\d\_]{1,5}[\.YRCc]{0,4}[\#\&\#\^]{0,2}/e",
"translate_indicator('\$0', $batchId)", /* if $batchId can be a string use 'batchId' */
$code);
function translate_indicator($s, $batchId) {
echo "translate_indicator($s, $batchId) invoked\n";
}
prints translate_indicator(AA1#, 2345) invoked.
You can also use preg_replace_callback and a class/an object
class Foo {
public $batchId = 2345;
public function translate_indicator($m) {
echo "batchId=$this->batchId . $m[0]\n";
}
}
$code = 'AA1#';
$foo = new Foo;
$code = preg_replace_callback(
'/[A-Za-z]{2,4}[\d\_]{1,5}[\.YRCc]{0,4}[\#\&\#\^]{0,2}/',
array($foo, 'translate_indicator'),
$code
);
As of php 5.3. you can also use an anonymous function + closure to "pass" the additional parameter.
$code = 'AA1#';
$batchId = 2345;
$code = preg_replace_callback(
'/[A-Za-z]{2,4}[\d\_]{1,5}[\.YRCc]{0,4}[\#\&\#\^]{0,2}/',
function($m) use ($batchId) {
echo "batchid=$batchId . code=$m[0]\n";
},
$code
);

try this instead
$batchId = 2345;
$code = preg_replace("/[A-Za-z]{2,4}[\d\_]{1,5}[\.YRCc]{0,4}[\#\&\#\^]{0,2}/e",
"translate_indicator('$0', {$batchId})", $code);
singly-quoted strings aren't expanded (i.e. $batchId won't be subsituted).

Use "translate_indicator('\$0', $batchId)" instead of 'translate_indicator(\'$0\', {$batchId})'.

Related

How to display string from function call inside concatenation?

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}";

PHP convert string into object sub sub variable

I have the following a PHP object with the following properties:
Object:
-Advanced
--Data
To access it in PHP I would have to do the following:
$object->Advanced->Data
Now I want to define a string which has a syntax like this:
$string = "Advanced->Data";
How do I proceed from here to be able to use:
$object->$string = "Something";
So that in the end
$object->Advanced->Data = "Something";
I couldn't figure out using eval or $object->{$string}
If I try to use $object->$string
PHP creates a new property called "Advanced->Data", basically not interpreting the -> Operator.
Though it is a hack, try this, it should work for your case
$arr = array();
$arr['Advanced']['Data'] = 'something';
$string = json_decode(json_encode($arr), 0);
echo $string->Advanced->Data;
Though it is a hack, this can also fetch your desire
$string = &$object->Advanced->Data;
$string = "here we go";
var_dump($object->Advanced->Data);
Probably eval() is not best solution, but it can be useful in your case:
class obj2 {
public $Data = 'test string';
}
class obj1 {
public $Advanced;
public function __construct() {
$this->Advanced = new obj2();
}
}
$test = new obj1();
$string1 = "\$test->Advanced->Data = 'new string';";
$string2 = "\$result = \$test->Advanced->Data;";
eval($string1);
eval($string2);
echo $result . PHP_EOL;
Output will be "new string".
Once try this,
$string = "Advanced->Data";
$arr = explode("->",$string);
$temp = $object->{$arr[0]}->$arr[1];
But this is specific condition. Let me know your requirement if this is not the answer.

php replacement route system

i need function in PHP for handle replacement something like this.
$pattern = ':foo/anotherString';
$replacement = array(
'foo' => 'HelloMe'
);
bazFunction($pattern, $replacement); // return 'HelloMe/anotherString';
this method used in some frameworks as route patterns. i want to know which function handle that.
this should do (5.3 required because of the closure)
function my_replace($pattern, $replacement) {
// add ':' prefix to every key
$keys = array_map(function($element) {
return ':' . $element;
}, array_keys($replacement));
return str_replace($keys, array_values($replacement), $pattern);
}
You wouldn't need this function if you pass the stuff directly to str_replace
str_replace(array(':foo'), array('HelloMe'), ':foo/anotherString');
$string = "foo/anotherString";
$replacement = array('foo','HelloMe');
$newString = str_replace($replacement[],,$string);
It seems that php got the function for you...
str_replace ( ":foo", "HelloMe" , $pattern )
will give you this output: HelloMe/anotherString

Backticking MySQL Entities

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.

trim url(query string)

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!

Categories