My PHP function to spin text

moomycow

New member
Jun 22, 2008
164
10
0
tweetingmachine.com
Hi guys,

Just spent 5 minutes knocking this up, you pass it some text in spinnable format, and it outputs a spun version:

PHP:
<?php
	function get_spin($input)
	{
		$pos = strpos($input, '{');
		$output = substr($input, 0, $pos);
		
		while($pos)
		{
			$close_pos = strpos($input, '}', $pos);
			$diff = $close_pos - $pos - 1;
			
			$words = explode('|', substr($input, $pos + 1, $diff));
			$word_num = mt_rand(1, count($words)) -1;
			
			$pos = strpos($input, '{', $pos+1);
			$output .= $words[$word_num];
			
			if ($pos)
			{
				$output .= substr($input, $close_pos + 1, ($pos - $close_pos - 1));
			}
		}
		
		$output .= substr($input, $close_pos +1 );
		return $output;
	}
	
	$input = file_get_contents('input.txt');
	echo get_spin($input);
	echo '<br/>';
	echo get_spin($input);
?>

Sample input: I am a {little|small|tiny|big fat}, {lovely|nice|horrible|smelly} {cow|bovine|bull} and I {say|shout} {moo|moo moo moo} to you!

Hope someone gets some use from it :)
 


You're welcome :)

My original plan was to have it look through a directory of spinnable articles and then output a file for each one containing many, many spins. I think both work as an idea :)
 
I've written a similar thing in Perl with regexes. Though I think your algo would be faster, because it uses substr(), the regex one would be easier to implement. I don't have PHP code, but the Perl code went something like this (did not test, going off memory):

Code:
$text = 'the {text|stuff} you want to spin';
$text =~ s/{([^}]+)}/spinit($1)/g;
sub spinit {
    my @words = split(/\|/, shift @_);
    return $words[rand @words];
}
The idea here is that you use a regex to find all text that is enclosed in {}, then you "remember" that text and replace it with the output of a function, into which you pass the "remembered" text. The function accepts the text, splits it by "|", as a result creating an array of variations. Then you return a random array member (variation). That returned value replaces everything in, and including {}.
 
I've written a similar thing in Perl with regexes. Though I think your algo would be faster, because it uses substr(), the regex one would be easier to implement. I don't have PHP code, but the Perl code went something like this (did not test, going off memory):

Code:
$text = 'the {text|stuff} you want to spin';
$text =~ s/{([^}]+)}/spinit($1)/g;
sub spinit {
    my @words = split(/\|/, shift @_);
    return $words[rand @words];
}
The idea here is that you use a regex to find all text that is enclosed in {}, then you "remember" that text and replace it with the output of a function, into which you pass the "remembered" text. The function accepts the text, splits it by "|", as a result creating an array of variations. Then you return a random array member (variation). That returned value replaces everything in, and including {}.

I thought of doing something with regular expressions, but was worried about the following case:

This is {a|one} test, I want {a|one} meal.

I wasn't sure how to do a replace on the text so that I'd do two random choices, instead of doing a random choice between {a|one} and replacing the other instance with that. I'm not too hot on regular expressions, so accept I could be worrying needlessly here ;)
 
{([^}]+)} -- that says { followed by anything other than }, which basically copies everything between {} into $1. You can also use a non-greedy quantifier:
{(.+?)}

I'm pretty sure PHP's regex were fully borrowed from Perl, so it should all work the same.
 
I'm pretty sure PHP's regex were fully borrowed from Perl, so it should all work the same.

PHP uses PCRE, which is the C regex library Perl uses, so yes, it'll work the same :P

Also, someone posted this a while ago, neat little spinner function that handles nested.
Code:
function spinText( $string ) {
    while ( preg_match( '/{([^{}]+)}/', $string, $matches ) ) {
        $parts = explode( '|', $matches[1] );
        $string = str_replace( $matches[0], $parts[ array_rand( $parts ) ], $string );
    }
    return $string;
}