parsing string into array (['class': 'navigation', 'id': 'navigation']) - php

Anyone can suggest an alternative method to parse a string: (['class': 'navigation', 'id': 'navigation']).
I used to use:
if(strpos($match[1], '[') === 0)
{
$render = array();
foreach(explode(',', substr($match[1], 1, -1)) as $arr)
{
$parts = explode(':', $arr);
if(count($parts) == 2)
{
$render[substr(trim($parts[0]), 1, -1)] = substr(trim($parts[1]), 1, -1);
}
else
{
$render[] = substr(trim($parts[0]), 1, -1);
}
}
$args[] = $render;
}
elseif(strpos($match[1], "'") == 0)
{
$args[] = substr($match[1], 1, -1);
}
However, it doesn't take long to understand the drawbacks of this method, e.g. ['title': 'Tom's diary'] would completely fail the code.
It would also nice to be able to identify an erroneous entries and leave them out. At the moment all I do is use: |{{([^}]+)}}| to catch all functions, which look like: {{foo}}, {{foo('test', 'best')}} or {{foo(['array': 'bar'])}}. If you have quick solution, I'd appreciate if you share it.

Your string's format seems not too far from JSON -- even if it doesn't seem to be real JSON (you are using [] instead of {}, arround what seems to be an object).
Maybe you could change your format, in order to use JSON ?
That would allow you to use the json_encode() and json_decode() functions.
And more people would be able to work with your data, as you'd be using a standard format.

This is a json string, just use json_decode to parse it into an array.

Related

How to format var_export to php5.4 array syntax

There are lots of questions and answers around the subject of valid php syntax from var outputs, what I am looking for is a quick and clean way of getting the output of var_export to use valid php5.4 array syntax.
Given
$arr = [
'key' => 'value',
'mushroom' => [
'badger' => 1
]
];
var_export($arr);
outputs
array (
'key' => 'value',
'mushroom' =>
array (
'badger' => 1,
),
)
Is there any quick and easy way to have it output the array as defined, using square bracket syntax?
[
'key' => 'value',
'mushroom' => [
'badger' => 1
]
]
Is the general consensus to use regex parsing? If so, has anyone come across a decent regular expression? The value level contents of the arrays I will use will all be scalar and array, no objects or classes.
I had something similar laying around.
function var_export54($var, $indent="") {
switch (gettype($var)) {
case "string":
return '"' . addcslashes($var, "\\\$\"\r\n\t\v\f") . '"';
case "array":
$indexed = array_keys($var) === range(0, count($var) - 1);
$r = [];
foreach ($var as $key => $value) {
$r[] = "$indent "
. ($indexed ? "" : var_export54($key) . " => ")
. var_export54($value, "$indent ");
}
return "[\n" . implode(",\n", $r) . "\n" . $indent . "]";
case "boolean":
return $var ? "TRUE" : "FALSE";
default:
return var_export($var, TRUE);
}
}
It's not overly pretty, but maybe sufficient for your case.
Any but the specified types are handled by the regular var_export. Thus for single-quoted strings, just comment out the string case.
For anyone looking for a more modern-day solution, use the Symfony var-exporter, also available as a standalone library on composer, but included in Symfony by default.
composer require symfony/var-exporter
use Symfony\Component\VarExporter\VarExporter;
// ...
echo VarExporter::export($arr)
I realize this question is ancient; but search leads me here. I didn't care for full iterations or using json_decode, so here's a preg_replace-based var_export twister that gets the job done.
function var_export_short($data, $return=true)
{
$dump = var_export($data, true);
$dump = preg_replace('#(?:\A|\n)([ ]*)array \(#i', '[', $dump); // Starts
$dump = preg_replace('#\n([ ]*)\),#', "\n$1],", $dump); // Ends
$dump = preg_replace('#=> \[\n\s+\],\n#', "=> [],\n", $dump); // Empties
if (gettype($data) == 'object') { // Deal with object states
$dump = str_replace('__set_state(array(', '__set_state([', $dump);
$dump = preg_replace('#\)\)$#', "])", $dump);
} else {
$dump = preg_replace('#\)$#', "]", $dump);
}
if ($return===true) {
return $dump;
} else {
echo $dump;
}
}
I've tested it on several arrays and objects. Not exhaustively by any measure, but it seems to be working fine. I've made the output "tight" by also compacting extra line-breaks and empty arrays. If you run into any inadvertent data corruption using this, please let me know. I haven't benchmarked this against the above solutions yet, but I suspect it'll be a good deal faster. Enjoy reading your arrays!
With https://github.com/zendframework/zend-code :
<?php
use Zend\Code\Generator\ValueGenerator;
$generator = new ValueGenerator($myArray, ValueGenerator::TYPE_ARRAY_SHORT);
$generator->setIndentation(' '); // 2 spaces
echo $generator->generate();
As the comments have pointed out, this is just an additional syntax. To get the var_export back to the bracket style str_replace works well if there are no ) in the key or value. It is still simple though using JSON as an intermediate:
$output = json_decode(str_replace(array('(',')'), array('&#40','&#41'), json_encode($arr)), true);
$output = var_export($output, true);
$output = str_replace(array('array (',')','&#40','&#41'), array('[',']','(',')'), $output);
I used the HTML entities for ( and ). You can use the escape sequence or whatever.

Extracting meaningful data from this complicated string in PHP

I'm receiving some structured data for my PHP application, but the format is somewhat unpredictable and difficult to deal with. I don't get a say in the initial format of the data. What I get is a string (sample given below).
[9484,'Víctor Valdés',8,[[['accurate_pass',[15]],['touches',[42]],['saves',[4]],['total_pass',[24]],['good_high_claim',[2]],['formation_place',[1]]]],1,'GK',1,0,0,'GK',31,183,78],[1320,'Carles Puyol',7.76,[[['accurate_pass',[50]],['touches',[75]],['aerial_won',[3]],['total_pass',[55]],['total_tackle',[1]],['formation_place',[6]]]],2,'DC',5,0,0,'D(CLR)',35,178,80],[5780,'Dani Alves',8.21,[[['accurate_pass',[58]],['touches',[99]],['total_scoring_att',[1]],['total_pass',[66]],['total_tackle',[6]],['aerial_lost',[1]],['fouls',[4]],['formation_place',[2]]]],2,'DR',22,0,0,'D(CR)',30,173,64],[83686,'Marc Bartra',8.31,[[['accurate_pass',[64]],['touches',[88]],['won_contest',[1]],['total_scoring_att',[1]],['aerial_won',[1]],['total_pass',[66]],['total_tackle',[5]],['aerial_lost',[1]],['fouls',[1]],['formation_place',[5]]]],2,'DC',15,0,0,'D(C)',22,181,70],[13471,'Adriano',6.72,[[['accurate_pass',[16]],['touches',[28]],['aerial_won',[2]],['total_pass',[18]],['total_tackle',[1]],['formation_place',[3]]]],2,'DL',21,1,31,'D(CLR),M(LR)',29,172,67]
The above is data for 5 football players. This is what I need to get:
[9484,'Víctor Valdés',8,[[['accurate_pass',[15]],['touches',[42]],['saves',[4]],['total_pass',[24]],['good_high_claim',[2]],['formation_place',[1]]]],1,'GK',1,0,0,'GK',31,183,78]
[1320,'Carles Puyol',7.76,[[['accurate_pass',[50]],['touches',[75]],['aerial_won',[3]],['total_pass',[55]],['total_tackle',[1]],['formation_place',[6]]]],2,'DC',5,0,0,'D(CLR)',35,178,80]
[5780,'Dani Alves',8.21,[[['accurate_pass',[58]],['touches',[99]],['total_scoring_att',[1]],['total_pass',[66]],['total_tackle',[6]],['aerial_lost',[1]],['fouls',[4]],['formation_place',[2]]]],2,'DR',22,0,0,'D(CR)',30,173,64]
[83686,'Marc Bartra',8.31,[[['accurate_pass',[64]],['touches',[88]],['won_contest',[1]],['total_scoring_att',[1]],['aerial_won',[1]],['total_pass',[66]],['total_tackle',[5]],['aerial_lost',[1]],['fouls',[1]],['formation_place',[5]]]],2,'DC',15,0,0,'D(C)',22,181,70]
[13471,'Adriano',6.72,[[['accurate_pass',[16]],['touches',[28]],['aerial_won',[2]],['total_pass',[18]],['total_tackle',[1]],['formation_place',[3]]]],2,'DL',21,1,31,'D(CLR),M(LR)',29,172,67]
Now, what I've done manually in the above example I need to do reliably with PHP. As you see, each player has a set of data. In order to split the big string into individual players, I can't just explode it by "],[" because that substring appears within each player's data too an unpredictable number of times.
Each player has a certain number of statistics (accurate_pass, touches etc) but they don't all have the same statistics. For instance, player #1 has "saves" and the others don't. Player #4 has "won_contest" and the others don't. There is no way to know who will have which stats. That means I can't just count commas until the new player or something similar.
Each player has a number before his name, but that number has an unpredictable number of digits and there's no way to discern it from other numbers which may appear in the string.
What I see as a constant occurrence for all players is the last bit: before the last closed bracket there are always 3 integers divided by commas. This type of substring (INT,INT,INT]) doesn't seem to appear in any other situation. Maybe this could be of some use?
A "hard" way to do this is parenthesis counting (less common in PHP, more common in text parsing languages)...
<?php
$str = "[9484,'Víctor Valdés',8,[[['accurate_pass',[15]],['touches',[42]],['saves',[4]],['total_pass',[24]],['good_high_claim',[2]],['formation_place',[1]]]],1,'GK',1,0,0,'GK',31,183,78],[1320,'Carles Puyol',7.76,[[['accurate_pass',[50]],['touches',[75]],['aerial_won',[3]],['total_pass',[55]],['total_tackle',[1]],['formation_place',[6]]]],2,'DC',5,0,0,'D(CLR)',35,178,80],[5780,'Dani Alves',8.21,[[['accurate_pass',[58]],['touches',[99]],['total_scoring_att',[1]],['total_pass',[66]],['total_tackle',[6]],['aerial_lost',[1]],['fouls',[4]],['formation_place',[2]]]],2,'DR',22,0,0,'D(CR)',30,173,64],[83686,'Marc Bartra',8.31,[[['accurate_pass',[64]],['touches',[88]],['won_contest',[1]],['total_scoring_att',[1]],['aerial_won',[1]],['total_pass',[66]],['total_tackle',[5]],['aerial_lost',[1]],['fouls',[1]],['formation_place',[5]]]],2,'DC',15,0,0,'D(C)',22,181,70],[13471,'Adriano',6.72,[[['accurate_pass',[16]],['touches',[28]],['aerial_won',[2]],['total_pass',[18]],['total_tackle',[1]],['formation_place',[3]]]],2,'DL',21,1,31,'D(CLR),M(LR)',29,172,67]";
$line = ',';
$paren_count = 0;
$lines = array();
for($i=0; $i<strlen($str); $i++)
{
$line.= $str{$i};
if($str{$i} == '[') $paren_count++;
elseif($str{$i} == ']')
{
$paren_count--;
if($paren_count == 0)
{
$lines[] = substr($line,1);
$line = '';
}
}
}
print_r($lines);
?>
Looks like #Boundless answer is correct, you can use json_decode, but you need to do a couple of things to the string you get first, which also seems like a valid json formatted string.
This worked for me:
<?php
$str = "[9484,'Víctor Valdés',8,[[['accurate_pass',[15]],['touches',[42]],['saves',[4]],['total_pass',[24]],['good_high_claim',[2]],['formation_place',[1]]]],1,'GK',1,0,0,'GK',31,183,78],[1320,'Carles Puyol',7.76,[[['accurate_pass',[50]],['touches',[75]],['aerial_won',[3]],['total_pass',[55]],['total_tackle',[1]],['formation_place',[6]]]],2,'DC',5,0,0,'D(CLR)',35,178,80],[5780,'Dani Alves',8.21,[[['accurate_pass',[58]],['touches',[99]],['total_scoring_att',[1]],['total_pass',[66]],['total_tackle',[6]],['aerial_lost',[1]],['fouls',[4]],['formation_place',[2]]]],2,'DR',22,0,0,'D(CR)',30,173,64],[83686,'Marc Bartra',8.31,[[['accurate_pass',[64]],['touches',[88]],['won_contest',[1]],['total_scoring_att',[1]],['aerial_won',[1]],['total_pass',[66]],['total_tackle',[5]],['aerial_lost',[1]],['fouls',[1]],['formation_place',[5]]]],2,'DC',15,0,0,'D(C)',22,181,70],[13471,'Adriano',6.72,[[['accurate_pass',[16]],['touches',[28]],['aerial_won',[2]],['total_pass',[18]],['total_tackle',[1]],['formation_place',[3]]]],2,'DL',21,1,31,'D(CLR),M(LR)',29,172,67]";
$str = '[' . $str . ']';
$str = str_replace('\'','"', $str);
//convert string to array
$arr = json_decode($str);
//now it's a php array so you can access any value
//echo '<pre>';
//print_r( $arr );
//echo '</pre>';
echo $arr [0][1]; //prints "Victor Valdes"
?>
Your string looks like JSON but it is not valid JSON so json_decode() will not work.
Your specific case could be converted to valid JSON by wrapping the string in a pair of [] and replacing the single quotes with double quotes:
$string = str_replace("'", '"', $your_string);
var_dump(json_decode('[' . $string . ']'));
See this example.
Of course the best solution would be to make sure that valid JSON is supplied because this will break easily if your text strings contain for example double quotes.
Try parsing as json, then pulling out what you want. Assuming that the data comes in blocks of 4 you can try:
$arr = json_decode($str);
for($i = 0; $i < count($arr) - 3; $i += 4)
{
$arr[] = new array($arr[$i], $arr[$i + 1], $arr[$i + 2], $arr[$i + 3]);
}
Why not count the [ in a loop? Here's a quick untested loop that could get you started.
$output = array('');
$brackets = 0;
$index = 0;
foreach (str_split($input) as $ch) {
if ($ch == '[') {
$brackets++;
}
$output[$index] .= $ch;
if ($ch == ']') {
$brackets--;
if ($brackets === 0) {
$index++;
$output[$index] = '';
}
}
}
Not very elegant though...

what is the efficient way to parse a template like this in php?

HTML template
<b><!--{NAME}--></b>
...
..
..
<b><!--{ADDRESS}--></b>
PHP Array
array('name'=>'my full name', ..... , 'address'=>'some address ');
I have lots of template files and have to parse each of them and replace it the str_replace given data in associative arrays.
I need your suggestions to improve this process or any other technique/tool that might be helpful
Edit: Current version of code
static function ParseTemplate($data,$template){
$html=$read==true ? self::GetCached($template,true) : $template ;
foreach($data as $key=>$value){
if(is_array($value) ){
foreach($data[$key] as $aval)
$html = str_replace("<!--{".$key."}-->",$aval,$html);
}
else $html = str_replace("<!--{".$key."}-->",$value,$html);
}
return $html;
}
thanks
Why not using a template engine such as Mustache, here for the PHP version
if the array keys are always the same as the template word inside the braces, do something like this:
foreach ($array as $key => $value) {
$html = str_replace("<!--{$key}-->", $value, $html)
}
if performance is important, it may be better to use strpos on the html, and go over the placeholders one by one. it will be quicker that doing str_replace many times on a large string. but if performance is not an issue, it's not necessary.
EDIT:
$index = strpos($html, "<!--");
while ( $index !== false ) {
// get the position of the end of the placeholder
$closing_index = strpos($html, "}-->", $index);
// extract the placeholder, which is the key in the array
$key = substr ($html, $index + 5, $closing_index);
// slice the html. the substr up to the placeholder + the value in the array
// + the substr after
$html = substr ($html, 0, $index) . $array[$key] .
substr ($html, $closing_index + 4);
$index = strpos($html, "<!--", $index + 1);
}
NOTE: this wasn't tested, so there may be some inaccuracies with the indexes... it's just to give you a general idea.
I think this is more efficient then str_replace, but you know what? this can use some benchmarking...
If I understand the question correctly, I think the following should work fine unless I'm missing something.
$a = array('name'=>'my full name','address'=>'some address');
foreach($a as $k=>$v)
{
$html = str_replace('<!--{'.strtoupper($k).'}-->',$v,$html);
}

String "array" to real array

Now I got the string of an array, like this :
$str = "array('a'=>1, 'b'=>2)";
How can I convert this string into real array ? Is there any "smart way" to do that, other that use explode() ? Because the "string" array could be very complicated some time.
Thanks !
Use php's "eval" function.
eval("\$myarray = $str;");
i don't know a good way to do this (only evil eval() wich realy should be avoided).
but: where do you get that string from? is it something you can affect? if so, using serialize() / unserialize() would be a much better way.
With a short version of the array json_decode works
json_decode('["option", "option2"]')
But with the old version just like the OP's asking it doesn't. The only thing it could be done is using Akash's Answer or eval which I don't really like using.
json_decode('array("option", "option2")')
You could write the string to a file, enclosing the string in a function definition within the file, and give the file a .php extension.
Then you include the php file in your current module and call the function which will return the array.
You'd have to use eval().
A better way to get a textual representation of an array that doesn't need eval() to decode is using json_encode() / json_decode().
If you can trust the string, use eval. I don't remember the exact syntax, but this should work.
$arr = eval($array_string);
If the string is given by user input or from another untrusted source, you should avoid eval() under all circumstances!
To store Arrays in strings, you should possibly take a look at serialize and unserialize.
Don't use eval() in any case
just call strtoarray($str, 'keys') for array with keys and strtoarray($str) for array which have no keys.
function strtoarray($a, $t = ''){
$arr = [];
$a = ltrim($a, '[');
$a = ltrim($a, 'array(');
$a = rtrim($a, ']');
$a = rtrim($a, ')');
$tmpArr = explode(",", $a);
foreach ($tmpArr as $v) {
if($t == 'keys'){
$tmp = explode("=>", $v);
$k = $tmp[0]; $nv = $tmp[1];
$k = trim(trim($k), "'");
$k = trim(trim($k), '"');
$nv = trim(trim($nv), "'");
$nv = trim(trim($nv), '"');
$arr[$k] = $nv;
} else {
$v = trim(trim($v), "'");
$v = trim(trim($v), '"');
$arr[] = $v;
}
}
return $arr;
}

How to find memory used by an object in PHP? (sizeof)

How to find memory used by an object in PHP? (c's sizeof). The object I want to find out about is a dictionary with strings and ints in it so it makes it hard to calculate it manually. Also string in php can be of varied length depending on encoding (utf8 etc) correct?
You could use memory_get_usage().
Run it once before creating your object, then again after creating your object, and take the difference between the two results.
To get an idea about the objects size, try
strlen(serialize($object));
It is by no means accurate, but an easy way to get a number for comparison.
If you need to know the size of an already created object or array, you can use the following code to find it out.
<?php
function rec_copy($src) {
if (is_string($src)) {
return str_replace('SOME_NEVER_OCCURING_VALUE_145645645734534523', 'XYZ', $src);
}
if (is_numeric($src)) {
return ($src + 0);
}
if (is_bool($src)) {
return ($src?TRUE:FALSE);
}
if (is_null($src)) {
return NULL;
}
if (is_object($src)) {
$new = (object) array();
foreach ($src as $key => $val) {
$new->$key = rec_copy($val);
}
return $new;
}
if (!is_array($src)) {
print_r(gettype($src) . "\n");
return $src;
}
$new = array();
foreach ($src as $key => $val) {
$new[$key] = rec_copy($val);
}
return $new;
}
$old = memory_get_usage();
$dummy = rec_copy($src);
$mem = memory_get_usage();
$size = abs($mem - $old);
?>
This essentially creates a copy of the array structure and all of its members.
A not 100% accurate, but still working version is also:
<?php
$old = memory_get_usage();
$dummy = unserialize(serialize($src));
$mem = memory_get_usage();
$size = abs($mem - $old);
Hope that helps for cases where the object is already build.
This method converts the array to a json string and determines the length of that string. The result should be fairly similar to the size of the array (both will have delimiters to partition members of the array or the stringified json)
strlen(json_encode(YourArray))
this method could be help you:
function getVariableUsage($var) {
$total_memory = memory_get_usage();
$tmp = unserialize(serialize($var));
return memory_get_usage() - $total_memory;
}
$var = "Hey, what's you doing?";
echo getVariableUsage($var);
https://www.phpflow.com/
I don't know that there is a simple way to get the size of an object in PHP. You might just have to do an algorith that
Counts the ints
Multiplies number of ints by size of an int on hard disk
Convert characters in strings to ASCII and
Multiply the ASCII values by how much they take up on disk
I'm sure there is a better way, but this would work, even though it would be a pain.

Categories