I need to run over some pages written in PHP by somebody that doesn't know that a variable should be initialized before use..
Therefore I have thousands of lines to check in order to make sure I Do not have such thing
<?php
$foo = $bar . "I m a noob";
?>
and of course having the problem with $bar not initialized.
The problem is that I have about 20~50 variables in this case in 25 files..
Do you know any super-hero way to set all the variables to ' ' or null ?
I do not want to set the warning level to E or W .. that's too crappy.
Thanks in advance!
Theoretically this code at the beginning of the script does the job:
$code = file_get_contents(__FILE__);
preg_match_all('/(?<=\$)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $code, $matches);
$variables = array_unique($matches[0]);
foreach ($variables as $variable) {
if (!isset($$variable)) {
$$variable = null;
}
}
The regular expression is from PHP site.
EDIT
A clean and quicker way, without a regular expression:
$code = file_get_contents(__FILE__);
$tokens = token_get_all($code);
foreach ($tokens as $token) {
if (is_array($token) && $token[0] == T_VARIABLE) {
$variable = ltrim($token[1], '$');
if (!isset($$variable)) {
$$variable = null;
}
}
}
you can use an array for the 25:
for ($i = 0; $i < 25; $i++) { $bar[$i] = ''; /* you can change the "" to null */ }
using an array with a for, you can set a great number of vars with diferente values, or with the same value.
Related
So, first off. I'm a C# programmer, so my PHP code is probably strongly influenced by how you would write in C#. Anyway, I'm trying to solve a problem where I've got a two-dimensional grid with two axis, x and y. The user gives input, and the program will tell you your new location.
For example, if user input was ' >^ ' the location would be 1,1.
What I also want the program to do, is to tell you the first location that you revisit. So, if you go from 0,0 -> 0,1 -> 0,0 -> 1,0 -> 1,1. Then the first location you revisited was 0,0.
My problem is that I'm trying to send my int to a function and then adding it to an array, and searching the array if the array contains the object. But it seems like the array is only adding one value (?)
declare(strict_types=1);
<?php
$NewInput = ">>^^<>>";
$arr = str_split($NewInput, 1);
$x = 0;
$y = 0;
Directions($x, $y, $arr);
function Directions (int $x, int $y, $arr)
{
foreach ($arr as $item)
{
if ($item == '^')
{
$y += 1;
}
elseif ($item == 'v')
{
$y -= 1;
}
elseif ($item == '>')
{
$x += 1;
}
elseif ($item == '<')
{
$x -= 1;
}
Previous($x,$y);
}
}
function Previous (string $x, string $y)
{
$Location = $x . '.' . $y;
echo "<br>";
$NewArray = array();
$NewArray[] = $Location;
$ReVisited = "";
if ($ReVisited == "")
{
if (array_search($Location, $NewArray))
{
$ReVisited = $Location;
echo $ReVisited;
}
}
echo $Location;
}
?>
What am I doing wrong?
And also if you see something overall with the code that could be improved please let me know.
Grateful for any tips or answers!
As with the other answer: The key reason your code isn't working is because you are re-initialising $newArray every time you call the function Previous() and hence the array is null.
To get around this you could use global as #Andrew suggested.
However, your code seems a little unorganised and overly complicated... There doesn't seem to be any need for two separate functions? The whole process is effectively one function.
If you treat it as one function then you have no need of using globals and you can do it in far fewer lines of code!
$NewInput = ">>^^<>>";
$arr = str_split($NewInput, 1);
$x = 0;
$y = 0;
directions($x, $y, $arr);
function directions($x, $y, $arr){
$used_points = []; // Array of points which have been passed through
$repeats = []; // Array of points which have been repeated
foreach ($arr as $item) {
switch ($item) {
case '^':
$y += 1;
break;
case 'v':
$y -= 1;
break;
case '>':
$x += 1;
break;
case '<':
$x -= 1;
break;
}
$current_point = "{$x}.{$y}";
$repeat = in_array($current_point, $used_points); // Checks if the point is repeated
echo $current_point . (($repeat) ? " REPEAT\n" : "\n"); // Outputs current point and whether it's a repeat
$used_points[] = $current_point; // Add current point to $used_points array
}
}
/* Output:
1.0
2.0
2.1
2.2
1.2
2.2 REPEAT
3.2
*/
Additional Explanation
SWITCH
The switch statement is effectively just another way of writing your if...elseif statements in a way which is easier to read. Functionally it does the same job (i.e. comparing a given value against another).
The three code examples below show different ways of comparing the values. The third example is effectively what a switch statement is.
N.B. break is required in the switch and while statements to exit the statement. If you omit it then the code continues to run further down the page than you may have intended.
## Example code with switch
switch ($test_case){
case 1:
// Do something if $test_case == 1...
break;
case 2:
// Do something if $test_case == 2...
break;
}
## Example code with if/elseif
if($test_case == 1){
// Do something if $test_case == 1...
}
elseif($test_case == 2){
// Do something if $test_case == 2...
}
## Example of if statements in a loop equivalent to switch
while(TRUE){
if($test_case == 1){
// Do something if $test_case == 1...
break;
}
if($test_case == 2){
// Do something if $test_case == 2...
break;
}
break;
}
{ curly braces } and double quotes
You can reference variables directly inside of double quotes; this often times makes code easier to read and nicer to look at. Example:
echo $a . ", " . $b . ", " . $c . ", " $d;
echo "$a, $b, $c, $d";
There are however instances when doing the above can give unexpected results. The curly braces help to keep that to a minimum and is just a good practice to get into - they clearly specify which variable you mean to reference.
$current_point = "$x.$y"; // is equivalent to....
$current_point = "{$x}.{$y}";
in_array($needle, $haystack)
in_array is simply a way to check whether or not a value is present in an array. If the value is found then the function returns TRUE if not then FALSE.
The line...
$repeat = in_array($current_point, $used_points);
...therefore sets $repeat to either TRUE or FALSE.
Ternary Operator
The ternary operator is effectively a short hand if...else statement.
## Example of if/else
$output = "";
if($value == TRUE){
$output = 1;
}
else{
$output = 2;
}
## Example with ternary operator
$output = ($value) ? 1 : 2;
In your case we use it to check if $repeat is TRUE (i.e. the current coordinate is present in the already visited coordinates array). If it is TRUE then we output REPEAT\n or \n by itself if it isn't.
echo ($repeat) ? " REPEAT\n" : "\n";
The whole line...
echo $current_point . (($repeat) ? " REPEAT\n" : "\n");
...then outputs the $current_point followed by either REPEAT\n or \n.
I'm not 100% if I understand correctly your desired outcome, but here are the problems I see:
You keep redefining $NewArray in the Previous function, so every time you run it, the array will become empty. You have to put that variable outside the function, and refer to it with the global scope. I also renamed it to $previousLocations. Here is the new function:
$previousLocations = array();
function Previous (string $x, string $y)
{
global $previousLocations;
$Location = $x . '.' . $y;
echo "<br>";
if(in_array($Location, $previousLocations)) {
// Already visited location
echo "Revisiting: " . $Location;
} else {
$previousLocations[] = $Location;
echo $Location;
}
}
Output:
1.0
2.0
2.1
2.2
1.2
Revisiting: 2.2
3.2
Please correct me if I misunderstood something.
I am creating a script that will locate a field in a text file and get the value that I need.
First used the file() function to load my txt into an array by line.
Then I use explode() to create an array for the strings on a selected line.
I assign labels to the array's to describe a $Key and a $Value.
$line = file($myFile);
$arg = 3
$c = explode(" ", $line[$arg]);
$key = strtolower($c[0]);
if (strpos($c[2], '~') !== false) {
$val = str_replace('~', '.', $c[2]);
}else{
$val = $c[2];
}
This works fine but that is a lot of code to have to do over and over again for everything I want to get out of the txt file. So I wanted to create a function that I could call with an argument that would return the value of $key and $val. And this is where I am failing:
<?php
/**
* #author Jason Moore
* #copyright 2014
*/
global $line;
$key = '';
$val = '';
$myFile = "player.txt";
$line = file($myFile); //file in to an array
$arg = 3;
$Character_Name = 3
function get_plr_data2($arg){
global $key;
global $val;
$c = explode(" ", $line[$arg]);
$key = strtolower($c[0]);
if (strpos($c[2], '~') !== false) {
$val = str_replace('~', '.', $c[2]);
}else{
$val = $c[2];
}
return;
}
get_plr_data2($Character_Name);
echo "This character's ",$key,' is ',$val;
?>
I thought that I covered the scope with setting the values in the main and then setting them a global within the function. I feel like I am close but I am just missing something.
I feel like there should be something like return $key,$val; but that doesn't work. I could return an Array but then I would end up typing just as much code to the the info out of the array.
I am missing something with the function and the function argument to. I would like to pass and argument example : get_plr_data2($Character_Name); the argument identifies the line that we are getting the data from.
Any help with this would be more than appreciated.
::Updated::
Thanks to the answers I got past passing the Array.
But my problem is depending on the arguments I put in get_plr_data2($arg) the number of values differ.
I figured that I could just set the Max of num values I could get but this doesn't work at all of course because I end up with undefined offsets instead.
$a = $cdata[0];$b = $cdata[1];$c = $cdata[2];
$d = $cdata[3];$e = $cdata[4];$f = $cdata[5];
$g = $cdata[6];$h = $cdata[7];$i = $cdata[8];
$j = $cdata[9];$k = $cdata[10];$l = $cdata[11];
return array($a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l);
Now I am thinking that I can use the count function myCount = count($c); to either amend or add more values creating the offsets I need. Or a better option is if there was a way I could generate the return array(), so that it would could the number of values given for array and return all the values needed. I think that maybe I am just making this a whole lot more difficult than it is.
Thanks again for all the help and suggestions
function get_plr_data2($arg){
$myFile = "player.txt";
$line = file($myFile); //file in to an array
$c = explode(" ", $line[$arg]);
$key = strtolower($c[0]);
if (strpos($c[2], '~') !== false) {
$val = str_replace('~', '.', $c[2]);
}else{
$val = $c[2];
}
return array($key,$val);
}
Using:
list($key,$val) = get_plr_data2(SOME_ARG);
you can do this in 2 way
you can return both values in an array
function get_plr_data2($arg){
/* do what you have to do */
$output=array();
$output['key'] =$key;
$output['value']= $value;
return $output;
}
and use the array in your main function
you can use reference so that you can return multiple values
function get_plr_data2($arg,&$key,&$val){
/* do job */
}
//use the function as
$key='';
$val='';
get_plr_data2($arg,$key,$val);
what ever you do to $key in function it will affect the main functions $key
I was over thinking it. Thanks for all they help guys. this is what I finally came up with thanks to your guidance:
<?php
$ch_file = "Thor";
$ch_name = 3;
$ch_lvl = 4;
$ch_clss = 15;
list($a,$b)= get_char($ch_file,$ch_name);//
Echo $a,': ',$b; // Out Puts values from the $cdata array.
function get_char($file,$data){
$myFile = $file.".txt";
$line = file($myFile);
$cdata = preg_split('/\s+/', trim($line[$data]));
return $cdata;
}
Brand new to this community, thanks for all the patience.
I want to dynamically access value of variable, let's say I have this array:
$aData = array(
'test' => 123
);
Standard approach to print the test key value would be:
print $aData['test'];
However, if I have to work with string representation of variable (for dynamic purposes)
$sItem = '$aData[\'test\']';
how can I achieve to print aData key named test? Neither of examples provided below works
print $$sItem;
print eval($sItem);
What would be the solution?
Your eval example is lacking the return value:
print eval("return $sItem;");
should do it:
$aData['test'] = 'foo';
$sItem = '$aData[\'test\']';
print eval("return $sItem;"); # foo
But it's not recommended to use eval normally. You can go into hell's kitchen with it because eval is evil.
Instead just parse the string and return the value:
$aData['test'] = 'foo';
$sItem = '$aData[\'test\']';
$r = sscanf($sItem, '$%[a-zA-Z][\'%[a-zA-Z]\']', $vName, $vKey);
if ($r === 2)
{
$result = ${$vName}[$vKey];
}
else
{
$result = NULL;
}
print $result; # foo
This can be done with some other form of regular expression as well.
As your syntax is very close to PHP an actually a subset of it, there is some alternative you can do if you want to validate the input before using eval. The method is to check against PHP tokens and only allow a subset. This does not validate the string (e.g. syntax and if a variable is actually set) but makes it more strict:
function validate_tokens($str, array $valid)
{
$vchk = array_flip($valid);
$tokens = token_get_all(sprintf('<?php %s', $str));
array_shift($tokens);
foreach($tokens as $token)
if (!isset($vchk[$token])) return false;
return true;
}
You just give an array of valid tokens to that function. Those are the PHP tokens, in your case those are:
T_LNUMBER (305) (probably)
T_VARIABLE (309)
T_CONSTANT_ENCAPSED_STRING (315)
You then just can use it and it works with more complicated keys as well:
$aData['test'] = 'foo';
$aData['te\\\'[]st']['more'] = 'bar';
$sItem = '$aData[\'test\']';
$vValue = NULL;
if (validate_tokens($sItem, array(309, 315, '[', ']')))
{
$vValue = eval("return $sItem;");
}
I used this in another answer of the question reliably convert string containing PHP array info to array.
No eval necessary if you have (or can get) the array name and key into separate variables:
$aData = array(
'test' => 123
);
$arrayname = 'aData';
$keyname = 'test';
print ${$arrayname}[$keyname]; // 123
You can just use it like an ordinary array:
$key = "test";
print $aData[$key];
Likewise $aData could itself be an entry in a larger array store.
As alternative, extracting the potential array keys using a regex and traversing an anonymous array (should have mentioned that in your question, if) with references would be possible. See Set multi-dimensional array by key path from array values? and similar topics.
Personally I'm using a construct like this to utilize dynamic variable paths like varname[keyname] instead (similar to how PHP interprets GET parameters). It's just an eval in sheeps clothing (do not agree with the eval scaremongering though):
$val = preg_replace("/^(\w)+(\[(\w+)])$/e", '$\1["\3"]', "aData[test]");
The only solution in your case is to use Eval().
But please be very very very careful when doing this! Eval will evaluate (and execute) any argument you pass to it as PHP. So if you will feed it something that comes from users, then anyone could execute any PHP code on your server, which goes without saying is a security hole the size of Grand canyon!.
edit: you will have to put a "print" or "echo" inside your $sItem variable somehow. It will either have to be in $sItem ($sItem = 'echo $aData[\'test\']';) or you will have to write your Eval() like this: Eval ( 'echo ' . $sData ).
$sItem = '$aData[\'test\']';
eval('$someVar = '.$sItem.';');
echo $someVar;
Use eval() with high caution as others aldready explained.
You could use this method
function getRecursive($path, array $data) {
// transform "foo['bar']" and 'foo["bar"]' to "foo[bar]"
$path = preg_replace('#\[(?:"|\')(.+)(?:"|\')\]#Uis', '[\1]', $path);
// get root
$i = strpos($path, '[');
$rootKey = substr($path, 0, $i);
if (!isset($data[$rootKey])) {
return null;
}
$value = $data[$rootKey];
$length = strlen($path);
$currentKey = null;
for (; $i < $length; ++$i) {
$char = $path[$i];
switch ($char) {
case '[':
if ($currentKey !== null) {
throw new InvalidArgumentException(sprintf('Malformed path, unexpected "[" at position %u', $i));
}
$currentKey = '';
break;
case ']':
if ($currentKey === null) {
throw new InvalidArgumentException(sprintf('Malformed path, unexpected "]" at position %u', $i));
}
if (!isset($value[$currentKey])) {
return null;
}
$value = $value[$currentKey];
if (!is_array($value)) {
return $value;
}
$currentKey = null;
break;
default:
if ($currentKey === null) {
throw new InvalidArgumentException(sprintf('Malformed path, unexpected "%s" at position %u', $char, $i));
}
$currentKey .= $char;
break;
}
}
if ($currentKey !== null) {
throw new InvalidArgumentException('Malformed path, must be and with "]"');
}
return $value;
}
How Can I get the string that present the variable in function argument?
example
function dbg($param){
return "param";
}
$var="Hello World";
echo dgb($var);
output: var
$arr=array();
echo dgb($arr);
output: arr
It is NOT possible to do what you ask reliably.
The closest you can come up to doing that is to do a debug_backtrace() to check which file called the function then tokenize the source of that file to find the reference.
Again, this will not work reliably. It will fail if you have multiple calls on one line. Truthfully, it isn't work the trouble. You are writing the code anyway, you know which variable you are passing.
function dbg($var) {
$bt = debug_backtrace();
$file = $bt[0]['file'];
$line = $bt[0]['line'];
$source = file_get_contents($file);
$tmpTokens = token_get_all($source);
$tokens = array ();
foreach ($tmpTokens as $token) {
if (is_array($token) && $token[0] !== T_INLINE_HTML && $token[0] !== T_WHITESPACE) {
$tokens[] = $token;
}
}
unset($tmpTokens);
$countTokens = count($tokens);
for ($i = 0; $i < $countTokens; $i++) {
if ($tokens[$i][3] > $line || $i === $countTokens - 1) {
$x = $i - (($tokens[$i][3] > $line) ? 1 : 0);
for ($x; $x >= 0; $x--) {
if ($tokens[$x][0] === T_STRING && $tokens[$x][1] === __FUNCTION__) {
if ($x !== $countTokens - 1 && $tokens[$x + 1][0] === T_VARIABLE) {
return $tokens[$x + 1][1];
}
}
}
}
}
return null;
}
printing a variable, you're doing it wrong.
the right ways is like this:
function dbg($param){
return $param;
}
$var="Hello World";
echo dgb($var);
and by the way you're passing an array to a function that only accepts variables. and oh it's a null array all the way worse!
I'm just guessing that you're trying to make a custom debugger which as a nice touch prints the name of the variable you're debugging. Well, that's not really possible. I'd suggest you look at debug_backtrace, which allows you to print the file, line number, function name and arguments of the place where you invoked your dbg function. Unless you use dbg more than once per line that helps you find the information you're looking for, and IMO is a lot more useful anyway.
Alternatively, you can have both the name and value of your variable if you call your function like this:
function dbg($var) {
echo 'name: ' . key($var) . ', value: ' . current($var);
}
$foo = 'bar';
dbg(compact('foo'));
You question doesnt make much sense, you may want to reword it.
It sounds like you want to use the $param in the function in which case you can just do something link echo $param;
I am very new to regex, and this is way too advanced for me. So I am asking the experts over here.
Problem
I would like to retrieve the constants / values from a php define()
DEFINE('TEXT', 'VALUE');
Basically I would like a regex to be able to return the name of constant, and the value of constant from the above line. Just TEXT and VALUE . Is this even possible?
Why I need it? I am dealing with language file and I want to get all couples (name, value) and put them in array. I managed to do it with str_replace() and trim() etc.. but this way is long and I am sure it could be made easier with single line of regex.
Note: The VALUE may contain escaped single quotes as well. example:
DEFINE('TEXT', 'J\'ai');
I hope I am not asking for something too complicated. :)
Regards
For any kind of grammar-based parsing, regular expressions are usually an awful solution. Even smple grammars (like arithmetic) have nesting and it's on nesting (in particular) that regular expressions just fall over.
Fortunately PHP provides a far, far better solution for you by giving you access to the same lexical analyzer used by the PHP interpreter via the token_get_all() function. Give it a character stream of PHP code and it'll parse it into tokens ("lexemes"), which you can do a bit of simple parsing on with a pretty simple finite state machine.
Run this program (it's run as test.php so it tries it on itself). The file is deliberately formatted badly so you can see it handles that with ease.
<?
define('CONST1', 'value' );
define (CONST2, 'value2');
define( 'CONST3', time());
define('define', 'define');
define("test", VALUE4);
define('const5', //
'weird declaration'
) ;
define('CONST7', 3.14);
define ( /* comment */ 'foo', 'bar');
$defn = 'blah';
define($defn, 'foo');
define( 'CONST4', define('CONST5', 6));
header('Content-Type: text/plain');
$defines = array();
$state = 0;
$key = '';
$value = '';
$file = file_get_contents('test.php');
$tokens = token_get_all($file);
$token = reset($tokens);
while ($token) {
// dump($state, $token);
if (is_array($token)) {
if ($token[0] == T_WHITESPACE || $token[0] == T_COMMENT || $token[0] == T_DOC_COMMENT) {
// do nothing
} else if ($token[0] == T_STRING && strtolower($token[1]) == 'define') {
$state = 1;
} else if ($state == 2 && is_constant($token[0])) {
$key = $token[1];
$state = 3;
} else if ($state == 4 && is_constant($token[0])) {
$value = $token[1];
$state = 5;
}
} else {
$symbol = trim($token);
if ($symbol == '(' && $state == 1) {
$state = 2;
} else if ($symbol == ',' && $state == 3) {
$state = 4;
} else if ($symbol == ')' && $state == 5) {
$defines[strip($key)] = strip($value);
$state = 0;
}
}
$token = next($tokens);
}
foreach ($defines as $k => $v) {
echo "'$k' => '$v'\n";
}
function is_constant($token) {
return $token == T_CONSTANT_ENCAPSED_STRING || $token == T_STRING ||
$token == T_LNUMBER || $token == T_DNUMBER;
}
function dump($state, $token) {
if (is_array($token)) {
echo "$state: " . token_name($token[0]) . " [$token[1]] on line $token[2]\n";
} else {
echo "$state: Symbol '$token'\n";
}
}
function strip($value) {
return preg_replace('!^([\'"])(.*)\1$!', '$2', $value);
}
?>
Output:
'CONST1' => 'value'
'CONST2' => 'value2'
'CONST3' => 'time'
'define' => 'define'
'test' => 'VALUE4'
'const5' => 'weird declaration'
'CONST7' => '3.14'
'foo' => 'bar'
'CONST5' => '6'
This is basically a finite state machine that looks for the pattern:
function name ('define')
open parenthesis
constant
comma
constant
close parenthesis
in the lexical stream of a PHP source file and treats the two constants as a (name,value) pair. In doing so it handles nested define() statements (as per the results) and ignores whitespace and comments as well as working across multiple lines.
Note: I've deliberatley made it ignore the case when functions and variables are constant names or values but you can extend it to that as you wish.
It's also worth pointing out that PHP is quite forgiving when it comes to strings. They can be declared with single quotes, double quotes or (in certain circumstances) with no quotes at all. This can be (as pointed out by Gumbo) be an ambiguous reference reference to a constant and you have no way of knowing which it is (no guaranteed way anyway), giving you the chocie of:
Ignoring that style of strings (T_STRING);
Seeing if a constant has already been declared with that name and replacing it's value. There's no way you can know what other files have been called though nor can you process any defines that are conditionally created so you can't say with any certainty if anything is definitely a constant or not nor what value it has; or
You can just live with the possibility that these might be constants (which is unlikely) and just treat them as strings.
Personally I would go for (1) then (3).
This is possible, but I would rather use get_defined_constants(). But make sure all your translations have something in common (like all translations starting with T), so you can tell them apart from other constants.
Try this regular expression to find the define calls:
/\bdefine\(\s*("(?:[^"\\]+|\\(?:\\\\)*.)*"|'(?:[^'\\]+|\\(?:\\\\)*.)*')\s*,\s*("(?:[^"\\]+|\\(?:\\\\)*.)*"|'(?:[^'\\]+|\\(?:\\\\)*.)*')\s*\);/is
So:
$pattern = '/\\bdefine\\(\\s*("(?:[^"\\\\]+|\\\\(?:\\\\\\\\)*.)*"|\'(?:[^\'\\\\]+|\\\\(?:\\\\\\\\)*.)*\')\\s*,\\s*("(?:[^"\\\\]+|\\\\(?:\\\\\\\\)*.)*"|\'(?:[^\'\\\\]+|\\\\(?:\\\\\\\\)*.)*\')\\s*\\);/is';
$str = '<?php define(\'foo\', \'bar\'); define("define(\\\'foo\\\', \\\'bar\\\')", "define(\'foo\', \'bar\')"); ?>';
preg_match_all($pattern, $str, $matches, PREG_SET_ORDER);
var_dump($matches);
I know that eval is evil. But that’s the best way to evaluate the string expressions:
$constants = array();
foreach ($matches as $match) {
eval('$constants['.$match[1].'] = '.$match[1].';');
}
var_dump($constants);
You might not need to go overboard with the regex complexity - something like this will probably suffice
/DEFINE\('(.*?)',\s*'(.*)'\);/
Here's a PHP sample showing how you might use it
$lines=file("myconstants.php");
foreach($lines as $line) {
$matches=array();
if (preg_match('/DEFINE\(\'(.*?)\',\s*\'(.*)\'\);/i', $line, $matches)) {
$name=$matches[1];
$value=$matches[2];
echo "$name = $value\n";
}
}
Not every problem with text should be solved with a regexp, so I'd suggest you state what you want to achieve and not how.
So, instead of using php's parser which is not really useful, or instead of using a completely undebuggable regexp, why not write a simple parser?
<?php
$str = "define('nam\\'e', 'va\\\\\\'lue');\ndefine('na\\\\me2', 'value\\'2');\nDEFINE('a', 'b');";
function getDefined($str) {
$lines = array();
preg_match_all('#^define[(][ ]*(.*?)[ ]*[)];$#mi', $str, $lines);
$res = array();
foreach ($lines[1] as $cnt) {
$p = 0;
$key = parseString($cnt, $p);
// Skip comma
$p++;
// Skip space
while ($cnt{$p} == " ") {
$p++;
}
$value = parseString($cnt, $p);
$res[$key] = $value;
}
return $res;
}
function parseString($s, &$p) {
$quotechar = $s[$p];
if (! in_array($quotechar, array("'", '"'))) {
throw new Exception("Invalid quote character '" . $quotechar . "', input is " . var_export($s, true) . " # " . $p);
}
$len = strlen($s);
$quoted = false;
$res = "";
for ($p++;$p < $len;$p++) {
if ($quoted) {
$quoted = false;
$res .= $s{$p};
} else {
if ($s{$p} == "\\") {
$quoted = true;
continue;
}
if ($s{$p} == $quotechar) {
$p++;
return $res;
}
$res .= $s{$p};
}
}
throw new Exception("Premature end of line");
}
var_dump(getDefined($str));
Output:
array(3) {
["nam'e"]=>
string(7) "va\'lue"
["na\me2"]=>
string(7) "value'2"
["a"]=>
string(1) "b"
}