Jump to content

str_to_array - Convert a string to an array


jchook

Recommended Posts

str_to_array()

 

Since I think this is an important feature that can be used in many ways (including WordPress Filters / Plugins), I thought I would come on here and post my str_to_array() function.

[it's my first post. I don't really need help at the moment, but the PHP Snippets forum is READ ONLY, so here I am.]

 

Using eval() as suggested here is dangerous. If you ran CMS content through eval(), any php-savvy author on the system could effectively gain admin access to the depths of your CMS and beyond. Hence my need for this function.

 

Features

  • Simplified syntax that is whitespace-insensitive like PHP
  • No regular expressions, so it's lightweight.
  • Eval functionality if you want it. The functionality can be disabled either through an if-statement or by commenting out that portion of the code.
  • Nested arrays for more complex applications
  • Escape character support (\) for putting special characters in your array content

 

# function str_to_array()
#
# Converts a simple string expression into an array.
# Supports simple syntax, nested arrays and nested PHP.
#
# @version 1.0
# @author  Wesley Roberts
# @date    July 11, 2010
# @link    http://fire.cowfight.com/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
function str_to_array($str, $var_sep=',', $val_sep='=') {

# Defaults
if (!is_string($str)) return null;
$r = array();

# Whitespace
$skip = array(" ","\t","\n","\r");

# Trim whitespace
$str = trim($str, implode($skip));

# Remove array notation if necessary
# NOTE that trim will not work here
# AND they are individual on purpose
if (substr($str,0,1) == '[')
	$str = substr($str,1);
if (substr($str,-1,1) == ']')
	$str = substr($str,0,-1);

# Always end the string with $var_sep
$str .= $var_sep;
$len = strlen($str);

# Defauts
$buf = ''; # buffer
$key = 0;  # next array key
$inc = 0;  # next numeric array key
$esc = false; # escaped char?

# Go through each character
for ($i=0; $i<$len; $i++) :

	if ($esc) :
		$buf .= $str[$i];
		$esc = false;
		continue;
	endif;

	switch($str[$i]):

		# Association operator
		case $val_sep:
			if (!empty($buf)) :
				$key = $buf;
				$buf = '';
			endif;
		break;

		# PHP! Note: this clears the buffer!!!
		# If you need to prefix your results,
		# do it within the braces.
		case '{' :
			$end = str_delimiter_pos_r($str, $len, $i, '{', '}');
			$buf = eval(substr($str, $i+1, $end-$i-1)); // excluding braces
			$i = $end;
		break;

		# Sub-array notation is recursive
		case '[':
			$end = str_delimiter_pos_r($str, $len, $i);
			$buf = str_to_array(substr($str, $i, $end-$i));
			$i = $end; # will be incremented
		break;

		# Quotations to have spaces
		case "'":
		case '"':
			$end = str_delimiter_pos_r($str, $len, $i, $str[$i], $str[$i]);
			$buf = substr($str, $i+1, $end-$i-1); // excluding quotes
			$i = $end;
		break;

		# Escape character
		case '\\':
			if (!$esc) :
				$esc = true;
				$buf .= $str[$i+1];
				$i++;
			else : 
				$buf .= '\\';
			endif;
		break;

		# Variable separator
		case $var_sep :
			$r[$key] = $buf;
			if (is_numeric($key))
				$inc++;
			$key = $inc;
			$buf = '';
		break;

		# Default
		default:
			if (! in_array($str[$i], $skip))
				$buf .= $str[$i];
		break;

	endswitch;

endfor;

# Return the final value
return $r;
}

# THIS FUNCTION IS REQUIRED BY str_to_array()
# Looks for the closing delimiter ($rchar). 
# Assumes that the first char ($offset=0) is the opening delimiter ($lchar).
#
function str_delimiter_pos_r(&$str, &$len=0, $offset=0, $lchar='[', $rchar=']') {
$r = $offset+1;
for ($r; $r<$len; $r++) :

	# Escape character
	if ($str[$r] == '\\')
		$r++;

	# Sub delimiters
	# (this only works when the delimiters are distinct)
	elseif (($str[$r] == $lchar) && ($lchar != $rchar))
		$r = str_delimiter_pos_r($str, $len, $r, $lchar, $rchar);

	# Whew. We made it.
	elseif ($str[$r] == $rchar)
		return $r;
endfor;
return $len;
}

 

 

It's probably best to learn by example for this one:

# The simplest usages
str_to_array('[]') // Array ()
str_to_array('[something]') // Array ( 'something' )
str_to_array('["something with spaces"]') // Array ( 'something with spaces' )
str_to_array('[ a=1, b=2, c=3]') // Array ( 'a'=>'1', 'b'=>'2', 'c'=>'3' )

 

For a more comprehensive display of its capabilities

print_r(str_to_array('
[
	first = [assigned, associatively],

	[an,array,all,alone],

	"something else" = [ array = values ],

	"just a value",

	"some php generated text" = {
		return "Today\'s Date Is ".date(\'m-d-Y\');
	}
]
'));

 

The above code will output:

/* OUTPUT:
Array
(
    [first] => Array
        (
            [0] => assigned
            [1] => associatively
        )

    [0] => Array
        (
            [0] => an
            [1] => array
            [2] => all
            [3] => alone
        )

    [something else] => Array
        (
            [array] => values
        )

    [1] => just a value
    [some php generated text] => Today's Date Is 07-11-2010
)

 

See Also

Link to comment
Share on other sites

You would use this function to accept user input in a CMS context. For example, suppose you have a gallery module that your users would like to include in their page content. You can specify a content filter that accepts input in a fashion that is very easy for a user to write (about as easy as BBCode).

 

This is a suggested syntax, though the syntax can be customized (the function is rather simple).

[my_module_name,  
    title = "My Custom Title",  
    gallery = "My First Gallery",
    height = 300,
    width = 500 ]

 

Moreover, since it is fairly robust (though certainly could use some improvements), this type of user input can be standardized across many plugins or modules to provide the user with some level of consistency.

Link to comment
Share on other sites

Can't figure out how to edit my first post, but I made some improvements just now. I think this thing has many uses if you just think along the lines of accepting user input. Whatever though, it's useful to me! Just sharing.

 

New in this version:

  • true and false now evaluate as actual boolean values. To use the string versions, just put quotes around them.
  • Fixed the empty array problem (returned an array with one element)
  • Fixed the weird escape character business
  • Fixed the argument order for str_delimiter_pos_r()

 

#
# function str_to_array()
#
# Converts an simple string expression into an array.
# Supports simple syntax, nested arrays and nested PHP.
#
# @version 1.1
# @author  Wesley Roberts
# @date    July 11, 2010
# @link    http://fire.cowfight.com/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
function str_to_array($str, $var_sep=',', $val_sep='=') {

# Defaults
if (!is_string($str)) return null;
$r = array();

# Whitespace
$skip = array(" ","\t","\n","\r");

# Trim whitespace
$str = trim($str, implode($skip));

# Remove array notation if necessary
# NOTE that trim will not work here
# AND they are individual on purpose
if (substr($str,0,1) == '[')
	$str = substr($str,1);
if (substr($str,-1,1) == ']')
	$str = substr($str,0,-1);

# Empty?
if (empty($str)) return array();

# Always end the string with $var_sep
$str.= $var_sep;
$len = strlen($str);

# Defauts
$buf = ''; # buffer
$key = 0;  # next array key
$inc = 0;  # next numeric array key
$esc = false; # escaped char?

# Go through each character
for ($i=0; $i<$len; $i++) :

	# Skip escaped characters
	if ($esc) :
		$buf .= $str[$i];
		$esc = false;
		continue;
	endif;

	# Check the current character
	switch($str[$i]):

		# Association operator
		case $val_sep:
			if (!empty($buf)) :
				$key = $buf;
				$buf = '';
			endif;
		break;

		# PHP! Note: this clears the buffer!!!
		# If you need to prefix your results,
		# do it within the braces.
		case '{' :
			$end = str_delimiter_pos_r($str, $i, '{', '}', $len);
			$buf = eval(substr($str, $i+1, $end-$i-1)); // excluding braces
			$i = $end;
		break;

		# Sub-array notation is recursive
		case '[':
			$end = str_delimiter_pos_r($str, $i, '[', ']', $len);
			$buf = str_to_array(substr($str, $i, $end-$i));
			$i = $end; # will be incremented
		break;

		# Quotations to have spaces
		case "'":
		case '"':
			$end = str_delimiter_pos_r($str, $i, $str[$i], $str[$i], $len);
			$buf = substr($str, $i+1, $end-$i-1); // excluding quotes
			$i = $end;
		break;

		# Escape character
		case '\\':
			$esc = true;
		break;

		# True
		case 't':
		case 'T':
			$look_for = 'true';
			$look_len = strlen($look_for);
			if (strtolower(substr($str, $i, $look_len)) == $look_for) :
				$buf = true;
				$i += $look_len - 1; # this will be incremented
			endif;
		break;

		# False
		case 'f':
		case 'F':
			$look_for = 'false';
			$look_len = strlen($look_for);
			if (strtolower(substr($str, $i, $look_len)) == $look_for) :
				$buf = false;
				$i += $look_len - 1; # this will be incremented
			endif;
		break;

		# Variable separator
		case $var_sep :
			$r[$key] = $buf;
			if (is_numeric($key))
				$inc++;
			$key = $inc;
			$buf = '';
		break;

		# Default
		default:
			if (! in_array($str[$i], $skip))
				$buf .= $str[$i];
		break;

	endswitch;

endfor;

# Return the final value
return $r;
}

# Looks for the closing delimiter ($rchar). 
# Assumes that the first char ($offset=0) is the opening delimiter ($lchar).
#
function str_delimiter_pos_r(&$str, $offset=0, $lchar='[', $rchar=']', &$len=0) {
$r = $offset+1;
for ($r; $r<$len; $r++) :

	# Escape character
	if ($str[$r] == '\\')
		$r++;

	# Sub delimiters
	# (this only works when the delimiters are distinct)
	elseif (($str[$r] == $lchar) && ($lchar != $rchar))
		$r = str_delimiter_pos_r($str, $r, $lchar, $rchar, $len);

	# Whew. We made it.
	elseif ($str[$r] == $rchar)
		return $r;
endfor;
return $len;
}

 

Link to comment
Share on other sites

New in Version 1.2

  • Now semi-whitespace-sensitve -- that is, whitespace is trimmed from the edges of values and keys. This greatly simplifies the syntax requirements and makes for generally easier writing and reading
  • Supports solo variables such as [ $var_name ]
  • Supports in-line variables wrapped in percent-signs such as %var_name%
  • Extracts additional variables to the local symbol table for use in building values or keys
  • Improved numeric key handling
  • Fixed poor support for true and false

 

The Code

#
# function str_to_array()
#
# Converts an simple string expression into an array.
# Supports simple syntax, nested arrays and nested PHP.
#
# @version 1.2
# @author  Wesley Roberts
# @date    July 11, 2010
# @link    http://fire.cowfight.com/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
function str_to_array($str, $opts=array()) {

# Defaults
if (!is_string($str)) return null;
$r = array();
$default_opts = array(
	'var_sep' => ',',
	'val_sep' => '=',
	'allow_eval' => true,
);
$opts = array_merge($default_opts, $opts);
extract($opts);

# Whitespace
$skip = array(" ","\t","\n","\r");

# Trim whitespace
$str = trim($str);

# Remove array notation if necessary
# NOTE that trim will not work here
# AND they are individual on purpose
if (substr($str,0,1) == '[')
	$str = substr($str,1);
if (substr($str,-1) == ']')
	$str = substr($str,0,-1);


# Empty?
if (empty($str)) return array();

# Always end the string with $var_sep
$str.= $var_sep;
$len = strlen($str);

# Defauts
$buf = ''; # buffer
$key = 0;  # next array key
$inc = 0;  # next numeric array key
$esc = false; # escaped char?
$quo = false; # quoted content? (i.e. can't be keyword)
$red = false; # have we read any non-space chars yet?

# Go through each character
for ($i=0; $i<$len; $i++) :

	# Skip escaped characters
	if ($esc) :
		$buf .= $str[$i];
		$esc = false;
		continue;
	endif;

	# Check the current character
	switch($str[$i]):

		# Association operator
		case $val_sep:
			if (!empty($buf)) :
				$key = trim($buf, implode($skip));
				$red = false;
				$buf = '';
			endif;
		break;

		# PHP! Note: this clears the buffer!!!
		# If you need to prefix your results,
		# do it within the braces.
		case '{' :
			$end = str_delimiter_pos_r($str, $i, '{', '}', $len);
			if ($allow_eval):
				$buf = eval(substr($str, $i+1, $end-$i-1)); // excluding braces
				$i = $end;
			endif;
		break;

		# Sub-array notation is recursive
		case '[':
			$end = str_delimiter_pos_r($str, $i, '[', ']', $len);
			$buf = str_to_array(substr($str, $i, $end-$i), $opts);
			$i = $end; # will be incremented
		break;

		# Variables with %
		case '%':
			$end = str_delimiter_pos_r($str, $i, '%', '%', $len);
			$var = substr($str, $i, $end-$i);
			$buf = $$var; # variable variable
			$i = $end; # will be incremented
		break;

		# Quotations allow spaces
		case "'":
		case '"':
			$end = str_delimiter_pos_r($str, $i, $str[$i], $str[$i], $len);
			$buf = substr($str, $i+1, $end-$i-1); // excluding quotes
			$i = $end;

			# Double quotes can be evaluated
			if ($str[$i] == '"' && $allow_eval) 
				$buf = eval('return "'.$buf.'";');
			else  $quo = true; # quoted
		break;

		# Escape character
		case '\\':
			$esc = true;
		break;

		# Variable separator (i.e. clear buffer)
		case $var_sep :

			# Keywords are possible when the input
			# is a single non-quoted word
			if (!$quo):
				if ($buf == 'true') :
					$buf = true;
				elseif ($buf == 'false') :
					$buf = false;
				elseif ($buf[0] == '$') :
					$var = substr($buf, 1);
					$buf = $$var; # variable variable
				endif;

			# If we're not in quotes, be more strict about
			# whitespace, but allow it in the middle.
			else : $buf = rtrim($buf, implode($skip)); 
			endif;

			# If the key was an integer, then we need
			# to find the next available integer for
			# the next entry's key 
			if (is_int($key)) :
				do { $inc++; } while(array_key_exists($inc, $r));
			endif;

			# Dump buffer into the returned array
			# associated with the appropriate key
			$r[$key] = $buf;

			# Prepare the next key
			$key = $inc;

			# Clear the buffer for the next entry
			$buf = '';
			$red = false;
		break;

		# Default. If we have started reading content
		# you can count in the spaces. Otherwise, f spaces
		default:
			if ($red || !in_array($str[$i], $skip)) :
				$red = true;
				$buf .= $str[$i];
			endif;
		break;

	endswitch;

endfor;

# Return the final value
return $r;
}

# Looks for the closing delimiter ($rchar). 
# Assumes that the first char ($offset=0) is the opening delimiter ($lchar).
#
function str_delimiter_pos_r(&$str, $offset=0, $lchar='[', $rchar=']', &$len=0) {
$r = $offset+1;
for ($r; $r<$len; $r++) :

	# Escape character
	if ($str[$r] == '\\')
		$r++;

	# Sub delimiters
	# (this only works when the delimiters are distinct)
	elseif (($str[$r] == $lchar) && ($lchar != $rchar))
		$r = str_delimiter_pos_r($str, $r, $lchar, $rchar, $len);

	# Whew. We made it.
	elseif ($str[$r] == $rchar)
		return $r;
endfor;
return $len;
}

 

 

Demonstration of Improvements

# version 1.1 syntax
# version 1.2 syntax

[ "some long key" = "some long string" ]
[ some long key = some long string ]

[ { return $variable; } ]
[ $variable ]

[ { return "something with $variables"; } ]
[ something with %variables% ]
[ "something with $variables" ]

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.