Skip to content

Instantly share code, notes, and snippets.

@irazasyed
Last active February 21, 2024 17:29
  • Star 76 You must be signed in to star a gist
  • Fork 48 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save irazasyed/11256369 to your computer and use it in GitHub Desktop.
PHP: Text Spinner Class - Nested spinning supported.
<?PHP
/**
* Spintax - A helper class to process Spintax strings.
*/
class Spintax
{
/**
* Set seed to make the spinner predictable.
*/
public function seed(mixed $seed)
{
mt_srand(crc32($seed));
}
public function process(string $text)
{
return preg_replace_callback(
'/\{(((?>[^\{\}]+)|(?R))*?)\}/x',
function ($match) {
$text = $this->process($match[1]);
$parts = explode('|', $text);
return $parts[mt_rand(0, count($parts) - 1)];
},
$text
);
}
/**
* If you want highest randomness,
* this is the method to use.
* Note: Seed has no effect.
*
* Last updated: 2023-10-26
*/
public function spin(string $text)
{
$pattern = '/{([^{}]+)}/';
while (preg_match($pattern, $text)) {
$text = preg_replace_callback($pattern, function ($matches) {
$options = explode('|', $matches[1]);
return $options[ random_int(0, count($options) - 1) ];
}, $text);
}
return $text;
}
}
/* EXAMPLE USAGE */
$spintax = new Spintax();
$string = '{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Davis}!';
echo $spintax->process($string);
/* NESTED SPINNING EXAMPLE */
echo $spintax->process('{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {{Jason|Malina|Sara}|Williams|Davis}');
@ranggacyber
Copy link

it's pretty cool .. many thanks

@salvatorecapolupo
Copy link

Interesting, thank you

@NealWalters
Copy link

Nice short code! Can you help explain the regex pattern: /{(((?>[^{}]+)|(?R))*)}/x
I'm trying to rewrite in Powershell/.NET. Especially, what is the ?R

@lury
Copy link

lury commented Mar 7, 2015

@NealWalters here you go:

/
  \{                    # opening curly ({) bracket
  (                     # group $1
    (                   # group $2
      (?>               # group $3
                        # Atomic group - Matches the longest possible substring in the group and doesn't allow later backtracking to reevaluate the group.
        [^\{\}]+        # one or more characters which are not "{" or "}"
      )
      |                 # or
      (?R)              # recursive match on the pattern itself - next nesting level of {}
    )*                  # zero or more times
  )
  \}                    # ended with curly (}) bracket
/x                      # PCRE_EXTENDED - whitespaces are ignored, # means comments

@soulfreeza
Copy link

thank you, anyway is it possible for making this spin work if only for first opening file and not working after it has been refresh ?

@erm3nda
Copy link

erm3nda commented Jul 5, 2015

Perfectly working. Thank you so much for the explanation about the regexp.
Regexp are really cool, but not so easy to understand/explain, also has a really HUGE syntax :)

@fer-ri
Copy link

fer-ri commented Sep 19, 2015

Simple but powerful :)

Thanks

@cantrikndelik
Copy link

[ask] it is possible to spin the title also ?

@andrija-naglic
Copy link

andrija-naglic commented Apr 21, 2016

If you want to use the [square|brackets|syntax] use this line in the process function:

'/\[(((?>[^\[\]]+)|(?R))*)\]/x',

thanks @lury

@duytanqb
Copy link

duytanqb commented Aug 1, 2016

Thank for very helpful code.
How can I use PHP to find match text in data then replace
example my content:
$content = 'The speedy black wolf bounded over the lazy hound';
$patten = array('{the|a|}',{black|red|yellow}....);

Expect result after replace : $content = '{the|a|} speedy {black|red|yellow} wolf bounded over the lazy hound';
they will find 1 of some word in {} and if 1 of some word by | match, they will replace to the content.

@b3rday
Copy link

b3rday commented Aug 3, 2016

thank you for this

@verma-ashish
Copy link

Is there way to ignore spin for "first capital alphabet word" (e,g. names, country, etc.)?

@iznubadd
Copy link

iznubadd commented Mar 20, 2017

hi
i love this scripts. is any ideas on how can it be implement with mysql data? like storing the synonyms in a database

more power

@herahadi
Copy link

hi, if i want to calculate maximum number of my spintax combination can generate unique article how to do that?

@haknudsen
Copy link

Is there a way I could show all the possible variations?

@fbparis
Copy link

fbparis commented Nov 10, 2017

The problem with this kind of spinners is that spuns do not have the same probability to be chosen.
For example, something like '{A|{B|C|D|E|F}}' will have 50% of 'A'.
I've coded another one with Python where every possible spuns have the same probability to be chosen, also you can retrieve the number of every possible spuns that can be generated from a spintax string.
You can have a look here : https://gist.github.com/fbparis/738c52e253639edb3cb41f90ea0b490c

@mamunur34
Copy link

Very interesting. Pretty cool. As it is very slim in size and heavy in working.

@patrykkp
Copy link

great! very useful - thanks!

@juanma386
Copy link

is posible? {
Mensaje001
|Mensaje 002
{
Mensaje 002-1
|
Mensaje 002-2
}
}
To infinity data spintax.

@kamiljanicki
Copy link

Hello! Generally class working great even with 3rd or 4th nesting level. But i think there is some mysterious bug.

Spintax works fine with short texts, but when text is longer than 8.000 character (whole text including synonims in brackets {}) then class cause HTTP errors (connecion reset).

I make a test in separated file where was only class definition, one object and variable with text.

Can somone help me with this?

In php ini i have declared 2GB of ram and unlimited time of execution.

Regards!

@PhiSYS
Copy link

PhiSYS commented Jul 3, 2019

I guess we could make the asterisk lazy for a little performance boost '/{(((?>[^\{\}]+)|(?R))*?)}/x'.

/\{(((?>[^\{\}]+)|(?R))*)\}/x 816 matches, 11424 steps (~30ms)
816 matches, 11424 steps, 30ms

/\{(((?>[^\{\}]+)|(?R))*?)\}/x 816 matches, 9792 steps (~13ms)
816 matches, 9792 steps, 13ms

@EmilioNicolas
Copy link

EmilioNicolas commented Jul 3, 2019

I guess we could make the asterisk lazy for a little performance boost '/{(((?>[^{}]+)|(?R))*?)}/x'.

I forked this optimized solution and added seed rand in order to fix the spin text with srand.

@jakama
Copy link

jakama commented Aug 4, 2020

Hello!

Great job. It is helping me a lot!

I'm trying to get the parts of the spintax text separately, but I only get the ones between {}

For example, in this text:
{Hi|Hello}{ Mr.|} Irfaq Syed. {How are you?|How about the day?}

I get:
{Hi | Hello}
{Mr. |}
{How are you? | How about the day?}

But not the text in the middle, that is, I don't get:

Irfaq Syed.

There is any Regex to get all, the spintax groups and the plain text?

Thanks!

@naufalkh
Copy link

naufalkh commented Jan 14, 2021

Very interesting!
How to make the reverse?
Input: Hello to you, Mr. Smith!
Output: {Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Davis}!

@herahadi
Copy link

herahadi commented Jul 5, 2022

how to output an array of all possible combinations?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment