Skip to content

Instantly share code, notes, and snippets.

@fer-ri
Created January 19, 2016 06:14
Show Gist options
  • Save fer-ri/13e0b1c88d59040dde10 to your computer and use it in GitHub Desktop.
Save fer-ri/13e0b1c88d59040dde10 to your computer and use it in GitHub Desktop.
Spinner PHP Class With Seed
<?php
$string = '{{The|A}} {{quick|speedy|fast}} {{brown|black|red}} {{fox|wolf}} {{jumped|bounded|hopped|skipped}} over the {{lazy|tired}} {{dog|hound}}';
echo '<p>';
for($i = 1; $i <= 5; $i++)
{
echo Spinner::detect($string, false).'<br />';
// or Spinner::flat($string, false).'<br />';
}
echo '</p>';
// nested
$string = '{{A {{simple|basic}} example|An uncomplicated scenario|The {{simplest|trivial|fundamental|rudimentary}} case|My {{test|invest{{igative|igation}}}} case}} to illustrate the {{function|problem}}';
echo '<p>';
for($i = 1; $i <= 5; $i++)
{
echo Spinner::detect($string, false).'<br />';
// or Spinner::nested($string, false).'<br />';
}
echo '</p>';
<?php
class Spinner
{
# Detects whether to use the nested or flat version of the spinner (costs some speed)
public static function detect($text, $seedPageName = true, $openingConstruct = '{{', $closingConstruct = '}}', $separator = '|')
{
if(preg_match('~'.$openingConstruct.'(?:(?!'.$closingConstruct.').)*'.$openingConstruct.'~s', $text))
{
return self::nested($text, $seedPageName, $openingConstruct, $closingConstruct, $separator);
}
else
{
return self::flat($text, $seedPageName, false, $openingConstruct, $closingConstruct, $separator);
}
}
# The flat version does not allow nested spin blocks, but is much faster (~2x)
public static function flat($text, $seedPageName = true, $calculate = false, $openingConstruct = '{{', $closingConstruct = '}}', $separator = '|')
{
# Choose whether to return the string or the number of permutations
$return = 'text';
if($calculate)
{
$permutations = 1;
$return = 'permutations';
}
# If we have nothing to spin just exit (don't use a regexp)
if(strpos($text, $openingConstruct) === false)
{
return $$return;
}
if(preg_match_all('!'.$openingConstruct.'(.*?)'.$closingConstruct.'!s', $text, $matches))
{
# Optional, always show a particular combination on the page
self::checkSeed($seedPageName);
$find = array();
$replace = array();
foreach($matches[0] as $key => $match)
{
$choices = explode($separator, $matches[1][$key]);
if($calculate)
{
$permutations *= count($choices);
}
else
{
$find[] = $match;
$replace[] = $choices[mt_rand(0, count($choices) - 1)];
}
}
if(!$calculate)
{
# Ensure multiple instances of the same spinning combinations will spin differently
$text = self::str_replace_first($find, $replace, $text);
}
}
return $$return;
}
# The nested version allows nested spin blocks, but is slower
public static function nested($text, $seedPageName = true, $openingConstruct = '{{', $closingConstruct = '}}', $separator = '|')
{
# If we have nothing to spin just exit (don't use a regexp)
if(strpos($text, $openingConstruct) === false)
{
return $text;
}
# Find the first whole match
if(preg_match('!'.$openingConstruct.'(.+?)'.$closingConstruct.'!s', $text, $matches))
{
# Optional, always show a particular combination on the page
self::checkSeed($seedPageName);
# Only take the last block
if(($pos = mb_strrpos($matches[1], $openingConstruct)) !== false)
{
$matches[1] = mb_substr($matches[1], $pos + mb_strlen($openingConstruct));
}
# And spin it
$parts = explode($separator, $matches[1]);
$text = self::str_replace_first($openingConstruct.$matches[1].$closingConstruct, $parts[mt_rand(0, count($parts) - 1)], $text);
# We need to continue until there is nothing left to spin
return self::nested($text, $seedPageName, $openingConstruct, $closingConstruct, $separator);
}
else
{
# If we have nothing to spin just exit
return $text;
}
}
# Similar to str_replace, but only replaces the first instance of the needle
private static function str_replace_first($find, $replace, $string)
{
# Ensure we are dealing with arrays
if(!is_array($find))
{
$find = array($find);
}
if(!is_array($replace))
{
$replace = array($replace);
}
foreach($find as $key => $value)
{
if(!empty($value))
{
if(($pos = mb_strpos($string, $value)) !== false)
{
# If we have no replacement make it empty
if(!isset($replace[$key]))
{
$replace[$key] = '';
}
$string = mb_substr($string, 0, $pos).$replace[$key].mb_substr($string, $pos + mb_strlen($value));
}
}
}
return $string;
}
private static function checkSeed($seedPageName)
{
# Don't do the check if we are using random seeds
if($seedPageName)
{
if($seedPageName === true)
{
mt_srand(crc32($_SERVER['REQUEST_URI']));
}
elseif($seedPageName == 'every second')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y-m-d-H-i-s')));
}
elseif($seedPageName == 'every minute')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y-m-d-H-i')));
}
elseif($seedPageName == 'hourly' OR $seedPageName == 'every hour')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y-m-d-H')));
}
elseif($seedPageName == 'daily' OR $seedPageName == 'every day')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y-m-d')));
}
elseif($seedPageName == 'weekly' OR $seedPageName == 'every week')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y-W')));
}
elseif($seedPageName == 'monthly' OR $seedPageName == 'every month')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y-m')));
}
elseif($seedPageName == 'annually' OR $seedPageName == 'every year')
{
mt_srand(crc32($_SERVER['REQUEST_URI'].date('Y')));
}
elseif(preg_match('!every ([0-9.]+) seconds!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / $matches[1])));
}
elseif(preg_match('!every ([0-9.]+) minutes!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / ($matches[1] * 60))));
}
elseif(preg_match('!every ([0-9.]+) hours!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / ($matches[1] * 3600))));
}
elseif(preg_match('!every ([0-9.]+) days!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / ($matches[1] * 86400))));
}
elseif(preg_match('!every ([0-9.]+) weeks!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / ($matches[1] * 604800))));
}
elseif(preg_match('!every ([0-9.]+) months!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / ($matches[1] * 2620800))));
}
elseif(preg_match('!every ([0-9.]+) years!', $seedPageName, $matches))
{
mt_srand(crc32($_SERVER['REQUEST_URI'].floor(time() / ($matches[1] * 31449600))));
}
else
{
throw new Exception($seedPageName. ' Was not a valid spin time option!');
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment