PHP Spintax Processor - php

I've been using the recurisve SpinTax processor as seen here, and it works just fine for smaller strings. However, it begins to run out of memory when the string goes beyond 20KB, and it's becoming a problem.
If I have a string like this:
{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Austin}!
and I want to have random combinations of the words put together, and not use the technique as seen in the link above (recursing through the string until there are no more words in curly-braces), how should I do it?
I was thinking about something like this:
$array = explode(' ', $string);
foreach ($array as $k=>$v) {
if ($v[0] == '{') {
$n_array = explode('|', $v);
$array[$k] = str_replace(array('{', '}'), '', $n_array[array_rand($n_array)]);
}
}
echo implode(' ', $array);
But it falls apart when there are spaces in-between the options for the spintax. RegEx seems to be the solution here, but I have no idea how to implement it and have much more efficient performance.
Thanks!

You could create a function that uses a callback within to determine which variant of the many potentials will be created and returned:
// Pass in the string you'd for which you'd like a random output
function random ($str) {
// Returns random values found between { this | and }
return preg_replace_callback("/{(.*?)}/", function ($match) {
// Splits 'foo|bar' strings into an array
$words = explode("|", $match[1]);
// Grabs a random array entry and returns it
return $words[array_rand($words)];
// The input string, which you provide when calling this func
}, $str);
}
random("{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Austin}!");
random("{This|That} is so {awesome|crazy|stupid}!");
random("{StackOverflow|StackExchange} solves all of my {problems|issues}.");

You can use preg_replace_callback() to specify a replacement function.
$str = "{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Austin}!";
$replacement = function ($matches) {
$array = explode("|", $matches[1]);
return $array[array_rand($array)];
};
$str = preg_replace_callback("/\{([^}]+)\}/", $replacement, $str);
var_dump($str);

Related

PHP array_map trim + parameters

I'm using array_map to trim all my array values but I need to pass a third parameter because I need to more than just trim whitespaces so I'm passing in a third parameter. Basically I want to trim all array values of whitespaces, single quotes, and double quotes.
I have a utility class where I created the function and it looks like this:
public function convertToArray($string, $trim = false) {
$split = explode(",", $string);
if($trim) {
$split = array_map("trim", $split, array(" '\""));
}
return $split;
}
Somehow I can't make this work though. I can still see double quotes in the result even though I followed the answer here.
I even tried
if($trim) {
$split = array_map("trim", $split);
$split = array_map("trim", $split, array("'"));
$split = array_map("trim", $split, array('"'));
}
but I still get the same result.
array_map takes a function that takes only one parameter. If you want to map your array with trim() with subsequent parameters different from the default ones, you have to wrap it with an anonymous function:
$split = array_map(function($item) {
return trim($item, ' \'"');
}, $split);
I think you would need to use an anonymous function for that :)
$split = array_map(function ($value) {
return trim($value, " '\"");
}, $split);
Just because this was exactly the same as the other answer, here is an alternative. This approach could be useful if this is an operation you're going to need in many different places ;)
function trim_spaces_and_quotes($value) {
return trim($value, " '\"");
}
$split = array_map('trim_spaces_and_quotes', $split);
I'd use array_walk, and you just have to append your additional characters to the existing defaults (from the docs):
array_walk($string_arr_to_trim, function (&$v) {
$v = trim($v, " \t\n\r\0\x0B'\"");
});

Why does this PHP spintax code repeat identical iterations?

http://ronaldarichardson.com/2011/09/23/recursive-php-spintax-class-3-0/
I like this script, but it isn't perfect. If you use this test input case:
{This is my {spintax|spuntext} formatted string, my {spintax|spuntext} formatted string, my {spintax|spuntext} formatted string example.}
You can see that the result ALWAYS contains 3 repetitions of either "spintax" or "spuntext". It never contains 1 "spintax" and 2 "spuntext", for example.
Example:
This is my spuntext formatted string, my spuntext formatted string, my spuntext formatted string example.
To be truly random it needs to generate a random iteration for each spintax {|} block and not repeat the same selection for identical blocks, like {spintax|spuntext}.
If you look at comment #7 on that page, fransberns is onto something, however when using his modified code in a live environment, the script would repeatedly run in an infinite loop and eat up all the server memory. So there must be a bug there, but I'm not sure what it is.
Any ideas? Or does anyone know of a robust PHP spintax script that allows for nested spintax and is truly random?
Please check this gist, it is working (and it is far simpler than original code ..).
The reason the Spintax class replaces all instances of {spintax|spuntext} with the same randomly chosen option is because of this line in the class:
$str = str_replace($match[0], $new_str, $str);
The str_replace function replaces all instances of the substring with the replacement in the search string. To replace only the first instance, progressing in a serial fashion as you desired, we need to use the function preg_replace with a passed "count" argument of 1. However, when I looked over your link to the Spintax class and reference to post #7 I noticed an error in his suggested augmentation to the Spintax class.
fransberns suggested replacing:
$str = str_replace($match[0], $new_str, $str);
with this:
//one match at a time
$match_0 = str_replace("|", "\|", $match[0]);
$match_0 = str_replace("{", "\{", $match_0);
$match_0 = str_replace("}", "\}", $match_0);
$reg_exp = "/".$match_0."/";
$str = preg_replace($reg_exp, $new_str, $str, 1);
The problem with fransbergs' suggestion is that in his code he did not properly construct the regular expression for the preg_replace function. His error came from not properly escaping the \ character. His replacement code should have looked like this:
//one match at a time
$match_0 = str_replace("|", "\\|", $match[0]);
$match_0 = str_replace("{", "\\{", $match_0);
$match_0 = str_replace("}", "\\}", $match_0);
$reg_exp = "/".$match_0."/";
$str = preg_replace($reg_exp, $new_str, $str, 1);
Consider replacing the original class with this augmented version utilizing my correction on fransberns' suggested replacemnet:
class Spintax {
function spin($str, $test=false)
{
if(!$test){
do {
$str = $this->regex($str);
} while ($this->complete($str));
return $str;
} else {
do {
echo "<b>PROCESS: </b>";var_dump($str = $this->regex($str));echo "<br><br>";
} while ($this->complete($str));
return false;
}
}
function regex($str)
{
preg_match("/{[^{}]+?}/", $str, $match);
// Now spin the first captured string
$attack = explode("|", $match[0]);
$new_str = preg_replace("/[{}]/", "", $attack[rand(0,(count($attack)-1))]);
// $str = str_replace($match[0], $new_str, $str); //this line was replaced
$match_0 = str_replace("|", "\\|", $match[0]);
$match_0 = str_replace("{", "\\{", $match_0);
$match_0 = str_replace("}", "\\}", $match_0);
$reg_exp = "/".$match_0."/";
$str = preg_replace($reg_exp, $new_str, $str, 1);
return $str;
}
function complete($str)
{
$complete = preg_match("/{[^{}]+?}/", $str, $match);
return $complete;
}
}
When I tried using fransberns' suggested replacement "as is", because of the improper escaping of the \ character, I got an infinite loop. I assume that this is where your memory problem came from. After correcting fransberns' suggested replacement with the correct escaping of the \ character I did not enter an infinite loop.
Try the class above with the corrected augmentation and see if it works on your server (I can't see a reason why it shouldn't).

How to: compare array elements with a String

I have predefined an array:
$tags = array('PHP', 'Webdesign', 'Wordpress', 'Drupal', 'SQL');
Now I am inputting a value into a text area:
$text = 'Working With Wordpress Shortcodes and doing some NoSQL and SQL';
How can I compare the string value with the predefined array?
The desired result I want based on the above is 'Wordpress' and 'SQL'
Can I do it in PHP and JQuery?
Also, if it will contain a regular expression like
Working With Wordpress; Shortcodes: and doing some NoSQL and ""SQL
Then what to do?
You should split your string like so
$array = explode( ' ', $text );
Then compare like so
$result_array = array_intersect($tags, $array);
Now result_array will contain every value that is in both of the arrays :)
Using explode split the string to works, than for each word check in_array
$words = explode(' ', $text);
$result = array();
foreach ($words as $word) {
if (in_array($word, $tags)) {
$result[] = $word;
}
}
Note: the comparision will be case-sensitivive
foreach(explode(' ', $text) as $word) {
if(in_array($word, $tags)) {
// This word is in our tags array, do something.
}
}
i have found some solution and it works perfectly for me.
Firstly i am rearranged the string with a "," separated using
var valsp = $('#desc').val();
var results = valsp.split(/\W+/);
after this just using inArray() i am getting the desired result
Hope it will help to others

mb_eregi_replace multiple matches get them

$string = 'test check one two test3';
$result = mb_eregi_replace ( 'test|test2|test3' , '<$1>' ,$string ,'i');
echo $result;
This should deliver: <test> check one two <test3>
Is it possible to get, that test and test3 was found, without using another match function ?
You can use preg_replace_callback instead:
$string = 'test check one two test3';
$matches = array();
$result = preg_replace_callback('/test|test2|test3/i' , function($match) use ($matches) {
$matches[] = $match;
return '<'.$match[0].'>';
}, $string);
echo $result;
Here preg_replace_callback will call the passed callback function for each match of the pattern (note that its syntax differs from POSIX). In this case the callback function is an anonymous function that adds the match to the $matches array and returns the substitution string that the matches are to be replaced by.
Another approach would be to use preg_split to split the string at the matched delimiters while also capturing the delimiters:
$parts = preg_split('/test|test2|test3/i', $string, null, PREG_SPLIT_DELIM_CAPTURE);
The result is an array of alternating non-matching and matching parts.
As far as I know, eregi is deprecated.
You could do something like this:
<?php
$str = 'test check one two test3';
$to_match = array("test", "test2", "test3");
$rep = array();
foreach($to_match as $val){
$rep[$val] = "<$val>";
}
echo strtr($str, $rep);
?>
This too allows you to easily add more strings to replace.
Hi following function used to found the any word from string
<?php
function searchword($string, $words)
{
$matchFound = count($words);// use tha no of word you want to search
$tempMatch = 0;
foreach ( $words as $word )
{
preg_match('/'.$word.'/',$string,$matches);
//print_r($matches);
if(!empty($matches))
{
$tempMatch++;
}
}
if($tempMatch==$matchFound)
{
return "found";
}
else
{
return "notFound";
}
}
$string = "test check one two test3";
/*** an array of words to highlight ***/
$words = array('test', 'test3');
$string = searchword($string, $words);
echo $string;
?>
If your string is utf-8, you could use preg_replace instead
$string = 'test check one two test3';
$result = preg_replace('/(test3)|(test2)|(test)/ui' , '<$1>' ,$string);
echo $result;
Oviously with this kind of data to match the result will be suboptimal
<test> check one two <test>3
You'll need a longer approach than a direct search and replace with regular expressions (surely if your patterns are prefixes of other patterns)
To begin with, the code you want to enhance does not seem to comply with its initial purpose (not at least in my computer). You can try something like this:
$string = 'test check one two test3';
$result = mb_eregi_replace('(test|test2|test3)', '<\1>', $string);
echo $result;
I've removed the i flag (which of course makes little sense here). Still, you'd still need to make the expression greedy.
As for the original question, here's a little proof of concept:
function replace($match){
$GLOBALS['matches'][] = $match;
return "<$match>";
}
$string = 'test check one two test3';
$matches = array();
$result = mb_eregi_replace('(test|test2|test3)', 'replace(\'\1\')', $string, 'e');
var_dump($result, $matches);
Please note this code is horrible and potentially insecure. I'd honestly go with the preg_replace_callback() solution proposed by Gumbo.

array_map with str_replace

Is it possible to use array_map in conjunction with str_replace without calling another function to do the str_replace?
For example:
array_map(str_replace(' ', '-', XXXXX), $myArr);
There is no need for array_map. From the docs: "If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well."
No, it's not possible. Though, if you are using PHP 5.3, you can do something like this:
$data = array('foo bar baz');
$data = array_map(function($value) { return str_replace('bar', 'xxx', $value); }, $data);
print_r($data);
Output:
Array
(
[0] => foo xxx baz
)
Sure it's possible, you just have to give array_map() the correct input for the callback function.
array_map(
'str_replace', // callback function (str_replace)
array_fill(0, $num, ' '), // first argument ($search)
array_fill(0, $num, '-'), // second argument ($replace)
$myArr // third argument ($subject)
);
But for the particular example in the question, as chiborg said, there is no need. str_replace() will happily work on an array of strings.
str_replace(' ', '-', $myArr);
Might be important to note that if the array being used in str_replace is multi-dimensional, str_replace won't work.
Though this doesn't directly answer the question of using array_map w/out calling an extra function, this function may still be useful in place of str_replace in array_map's first parameter if deciding that you need to use array_map and string replacement on multi-dimensional arrays. It behaves the same as using str_replace:
function md_str_replace($find, $replace, $array) {
/* Same result as using str_replace on an array, but does so recursively for multi-dimensional arrays */
if (!is_array($array)) {
/* Used ireplace so that searches can be case insensitive */
return str_ireplace($find, $replace, $array);
}
$newArray = array();
foreach ($array as $key => $value) {
$newArray[$key] = md_str_replace($find, $replace, $value);
}
return $newArray;
}

Categories