/php/phpJSO.php
PHP | 833 lines | 573 code | 71 blank | 189 comment | 83 complexity | aeb4f88c55ec94e111ab9a56ea358c56 MD5 | raw file
- <?php
- /**
- * phpJSO - The Javascript Obfuscator written in PHP. Although
- * it effectively obfuscates Javascript code, it is meant to compress
- * code to save disk space rather than hide code from end-users.
- *
- * @started: Mon, May 23, 2005
- * @copyright: Copyright (c) 2004-2006 Cortex Creations, All Rights Reserved
- * @website: www.cortex-creations.com/phpjso
- * @license: Free, zlib/libpng license - see LICENSE
- * @version: 0.9
- * @subversion: $Id: phpJSO.php 70 2006-10-10 01:35:37Z josh $
- */
- // See if we're getting called via the command line
- if (isset($_SERVER['SHELL']))
- {
- // If no arguments are provided, display help screen
- if (count($_SERVER['argv']) < 3)
- {
- print("php phpJSO.php <in-file> <out-file>\n\n");
- print("Other options:\n");
- print("\t-encoding-type={value} Possible values: 1 or 0. The default encoding is 1. 0 turns encoding off.\n");
- print("\t-fast-decompress={value} Possible values: 1 or 0. Defaults to 0. 1 is on, 0 is off.\n");
- print("\t-collapse-blocks={value} Possible values: 1 or 0. Defaults to 0. 1 is on, 0 is off.\n");
- print("\t-collapse-math={value} Possible values: 1 or 0. Defaults to 0. 1 is on, 0 is off.\n");
- }
- // Check all options, see if they should be on or off
- $options = array
- (
- array('param' => '-encoding-type', 'value' => 1),
- array('param' => '-fast-decompress', 'value' => 0),
- array('param' => '-collapse-blocks', 'value' => 0),
- array('param' => '-collapse-math', 'value' => 0)
- );
- foreach ($_SERVER['argv'] as $argument)
- {
- foreach ($options as $k=>$option)
- {
- if (preg_match('#'.preg_quote($option['param']).'\=(.*?)$#', $argument, $match))
- {
- $options[$k]['value'] = $match[1];
- }
- }
- }
- // Check that in-file exists and out-file is writable
- $in_file = $_SERVER['argv'][1];
- $out_file = $_SERVER['argv'][2];
- if (!file_exists($in_file))
- {
- die("Make sure that in-file ($in_file) exists.");
- }
- if (!(touch($out_file) && file_exists($out_file) && is_writable($out_file)))
- {
- die("Make sure that out-file ($out_file) can be written to.");
- }
- // Open file
- $in_file_code = file_get_contents($in_file);
-
- // Compress it
- $messages = array();
- $compressed_code = phpJSO_compress($in_file_code, $messages,
- $options[0]['value'],
- $options[1]['value'],
- $options[2]['value'],
- $options[3]['value']);
-
- // Save to out file
- $out_file_handle = fopen($out_file, 'w');
- fwrite($out_file_handle, "/**\n * {your code messages/copyright here}\n *\n * This code was compressed by phpJSO - www.cortex-creations.com.\n**/\n\n".$compressed_code);
- fclose($out_file_handle);
-
- // Report stats
- $message = '';
- if (count($messages))
- {
- print("Successfully compressed code.\n");
- foreach ($messages as $k=>$m)
- {
- print("\t - $m\n");
- }
- print("\nThank you for using phpJSO! Check www.cortex-creations.com for news and updates.");
- }
- }
- // Only do HTML output if UNIT_TEST constant is not present
- else if (!defined('UNIT_TEST'))
- {
- // Uncomment to profile using APD
- //apd_set_pprof_trace('/Users/joshuagross/Desktop/APD Traces');
- $phpJSO_version = '0.9';
- // Compress javascript from a submitted form
- $compressed_js = 'Compressed code will be placed here';
- $code = 'Place your code here.';
- $messages = array();
- if (isset($_REQUEST['jscode']))
- {
- // Get JS code
- $code = $_REQUEST['jscode'];
- // Strip slashes from input?
- if (get_magic_quotes_gpc())
- {
- $code = stripslashes($code);
- }
- // Compress
- $compressed_js = phpJSO_compress($code, $messages,
- (isset($_REQUEST['encoding_type']) ? $_REQUEST['encoding_type'] :'1'),
- (isset($_REQUEST['fast_decompress']) ? true : false),
- (isset($_REQUEST['collapse_blocks']) ? true : false),
- (isset($_REQUEST['collapse_math']) ? true : false));
- }
- $compressed_js = htmlspecialchars($compressed_js);
- $code = htmlspecialchars($code);
- // Format compression messages, if any
- $message = '';
- if (count($messages))
- {
- $message = '<b>Successfully compressed code.</b><br /><ul>';
- foreach ($messages as $k=>$m)
- {
- $message .= nl2br("<li>$m</li>");
- }
- $message .= '</ul><br /><br />';
- }
- // Get HTML value of fast_decompress checkbox
- $encoding_type = (isset($_REQUEST['encoding_type']) ? $_REQUEST['encoding_type'] : '1');
- $fast_decompress_value = (isset($_REQUEST['fast_decompress']) ? 'checked="checked"' : '');
- $collapse_blocks_value = (isset($_REQUEST['collapse_blocks']) ? 'checked="checked"' : '');
- $collapse_math_value = (isset($_REQUEST['collapse_math']) ? 'checked="checked"' : '');
- // Get encoding type select options
- $encoding_options = '';
- $encoding_options .= '<option value="1" '.($encoding_type == '1' ? 'selected="selected"' : '').'>Numeric encoding: smallest possible</option>';
- $encoding_options .= '<option value="off" '.($encoding_type == 'off' ? 'selected="selected"' : '').'>No encoding</option>';
- // Show forms, including any compressed JS
- print("
- <html>
- <head>
- <title>phpJSO version $phpJSO_version</title>
- <style type=\"text/css\">
- body {
- margin: 0px;
- padding: 20px;
- background-color: #ffffff;
- color: #000000;
- font-family: Verdana, Arial, Sans;
- font-size: 11px;
- }
- textarea {
- background-color: #dddddd;
- width: 100%;
- height: 40%;
- padding: 5px;
- color: #000000;
- font-family: Verdana, Arial, Sans;
- font-size: 11px;
- }
- </style>
- <script>
- </script>
- </head>
- <body>
- <form action=\"".$_SERVER['PHP_SELF']."\" method=\"post\">
- $message
-
- <b>Compressed Code:</b><br />
- <textarea rows=\"20\" cols=\"30\">$compressed_js</textarea><br /><br />
-
- <b>Place Your Code Here:</b><br />
- <textarea rows=\"20\" cols=\"30\" name=\"jscode\">$code</textarea><br /><br />
-
- <b><label for=\"id_encoding_type\">Encoding type: </label></b><select name=\"encoding_type\" id=\"id_encoding_type\">$encoding_options</select><br />
- This is the encoding type that will be used by phpJSO; it is simply how you want
- phpJSO to compress and encode your code. For VERY large code (6,000 lines or more without comments) either turn
- encoding off, or encode about 3,000 lines at a time. This will ensure that browsers
- execute the code quickly.
- Please note that encoding does NOT change how your code works AT ALL.<br /><br />
-
- <b><input type=\"checkbox\" name=\"fast_decompress\" id=\"id_fast_decompress\" $fast_decompress_value /><label for=\"id_fast_decompress\">Fast decompression</label></b><br />
- This option is recommended for large javascript files (above 1,000 lines of code without comments); the larger
- a script is, the longer it will take to decompress. you won't notice much of a speed
- difference with smaller scripts. note, however, that this option also makes the
- compressed code <i>slightly</i> larger. See the \"encoding type\" option; this
- option doesn't matter if you turn encoding off.<br /><br />
- <b><input type=\"checkbox\" name=\"collapse_blocks\" id=\"id_collapse_blocks\" $collapse_blocks_value /><label for=\"id_collapse_blocks\">Collapse Code Blocks</label></b><br />
- This option helps compress code to miniscule sizes. It \"collapses\" code blocks
- whenever possible. For example, <i>if(1){alert(1);}</i> becomes <i>if(1)alert(1);</i>.
- In short code it may not make a huge difference, but it can in longer code. It also
- makes phpJSO slightly slower.<br /><br />
- <b><input type=\"checkbox\" name=\"collapse_math\" id=\"id_collapse_math\" $collapse_math_value /><label for=\"id_collapse_math\">Collapse Math Constants</label></b><br />
- If you select this option (recommended), phpJSO will change code sections like \"1+1\" to \"2\",
- or \"100-(20+30)\" to \"50\". This is a very fast operation and can help reduce code size as
- well as speed up running times.<br /><br />
-
- <input type=\"submit\" value=\"Compress Code\" />
- </form>
- </body>
- </html>
- ");
- }
- /**
- * Main phpJSO compression function. Pass Javascript code to it, and it will
- * return compressed code.
- */
- function phpJSO_compress ($code, &$messages, $encoding_type, $fast_decompress, $collapse_blocks, $collapse_math_constants)
- {
- // Start timer
- $start_time = phpJSO_microtime_float();
-
- // Array of tokens - alphanumeric
- $tokens = array();
-
- // Array of only numeric tokens, that are only inserted to prevent being
- // wrongly replaced with another token. For example: the integer 0 will
- // be replaced with whatever is at token index 0.
- $numeric_tokens = array();
-
- // Save original code length
- $original_code_length = strlen($code);
-
- // Remove strings and multi-line comments from code before performing operations
- $str_array = array();
- phpJSO_strip_strings_and_comments($code, $str_array, substr(md5(time()), 10, 2));
-
- // Strip junk from JS code
- phpJSO_strip_junk($code, true);
- if ($collapse_blocks)
- {
- $collapsed_blocks = 0;
- $code = phpJSO_collapse_blocks($code, $collapsed_blocks);
- $messages[] = 'Block collapse mode on: ' . $collapsed_blocks . ' blocks were collapsed.';
- }
- phpJSO_strip_junk($code);
- // Compress math constants in code?
- if ($collapse_math_constants)
- {
- $collapsed_math_constants = 0;
- $code = phpJSO_collapse_math($code, $collapsed_math_constants);
- $messages[] = 'Math constant collapse mode on: ' . $collapsed_math_constants . ' math constants were collapsed.';
- }
-
- // Add strings back into code
- phpJSO_restore_strings($code, $str_array);
-
- // Compressed code
- $compressed_code = $code;
- // Should we encode?
- if ($encoding_type == '1')
- {
- // BUG FIX: If a modulus is in the code, it will break obfuscation because the browser treats it as escaping of characters
- $compressed_code = str_replace('%', '% ', $compressed_code);
-
- // Find all tokens in code
- phpJSO_get_tokens($compressed_code, $numeric_tokens, $tokens);
-
- // Insert numeric tokens into token array
- phpJSO_merge_token_arrays($tokens, $numeric_tokens);
- // Replace all tokens with their token index
- phpJSO_replace_tokens($tokens, $compressed_code);
-
- // We have to sort the array because it can end up looking like this:
- // (
- // [0] => var
- // ...
- // [5] => opera
- // [7] =>
- // [6] => domLib_isSafari
- // [8] => domLib_isKonq
- // )
- ksort($tokens);
- reset($tokens);
-
- // Insert decompression code
- $compressed_code_double_slash = '"'.str_replace(array('\\', '"'), array('\\\\', '\\"'), $compressed_code).'"';
- $compressed_code_single_slash = "'".str_replace(array('\\', "'"), array('\\\\', "\\'"), $compressed_code)."'";
- $compressed_code = (strlen($compressed_code_double_slash) < strlen($compressed_code_single_slash) ? $compressed_code_double_slash : $compressed_code_single_slash);
- if ($fast_decompress)
- {
- $messages[] = 'Fast decompression mode.';
- $compressed_code = "eval(function(a,b,c,d,e){if(!''.replace(/^/,String)){d=function(e){return c[e]&&typeof(c[e])=='string'?c[e]:e};b=1}while(b--)if(c[b]||d)a=a.replace(new RegExp(e+(d?'\\\\w+':b)+e,'g'),d||c[b]);return a}($compressed_code,".count($tokens).",'".implode('|',$tokens)."'.split('|'),0,'\\\\b'))";
- }
- else
- {
- $compressed_code = "eval(function(a,b,c,d){while(b--)if(c[b])a=a.replace(new RegExp(d+b+d,'g'),c[b]);return a}($compressed_code,".count($tokens).",'".implode('|',$tokens)."'.split('|'),'\\\\b'))";
- }
-
- // Which is smaller: compressed code or uncompressed code?
- if (strlen($code) < strlen($compressed_code))
- {
- $messages[] = 'The uncompressed code (with only comments and whitespace removed)
- was smaller than the fully compressed code.';
- $compressed_code = $code;
- }
- }
-
- // End timer
- $execution_time = phpJSO_microtime_float() - $start_time;
-
- // Message about how long compression took
- $messages[] = "Compressed code in $execution_time seconds.";
-
- // Message reporting compression sizes
- $compressed_length = strlen($compressed_code);
- $ratio = $compressed_length / $original_code_length;
- $messages[] = "Original code length: $original_code_length.
- Compressed code length: $compressed_length.
- Compression ratio: $ratio.";
-
- return $compressed_code;
- }
- /**
- * Strip strings and comments from code
- */
- function phpJSO_strip_strings_and_comments (&$str, &$strings, $comment_delim)
- {
- // Find all occurances of comments and quotes. Then loop through them and parse.
- $quotes_and_comments = phpJSO_sort_occurances($str, array('/', '//', '/*', '*/', '"', "'"));
- // Loop through occurances of quotes and comments
- $in_string = $last_quote_pos = $in_comment = $in_regex = false;
- $removed = 0;
- $num_strings = count($strings);
- $invalid = array();
- foreach ($quotes_and_comments as $location => $token)
- {
- // Parse strings
- if ($in_string !== false)
- {
- if ($token == $in_string)
- {
- // First, we'll pull out the string and save it, and replace it with a number.
- $replacement = '`' . $num_strings . '`';
- $string_start_index = $last_quote_pos - $removed;
- $string_length = ($location - $last_quote_pos) + 1;
- $strings[$num_strings] = substr($str, $string_start_index, $string_length);
- ++$num_strings;
- // Remove the string completely
- $str = substr_replace($str, $replacement, $string_start_index, $string_length);
- // Clean up time...
- $removed += $string_length - strlen($replacement);
- $in_string = $last_quote_pos = false;
- }
- }
- // Parse multi-line comments
- else if ($in_comment !== false)
- {
- // If it's the end of a comment, replace it with a single space
- // We replace it with a space in case a comment is between two tokens: test/**/test
- if ($token == '*/')
- {
- $comment_start_index = $in_comment - $removed;
- $comment_length = ($location - $in_comment) + 2;
- $str = substr_replace($str, ' ', $comment_start_index, $comment_length);
- $removed += $comment_length - 1;
- $in_comment = false;
- }
- }
- // Parse regex
- else if ($in_regex !== false)
- {
- // Should be end of the regex, unless it's escaped
- // If it is the end... don't do anything except stop parsing
- // We just don't want strings inside of regex to be removed,
- // like: /["']*/ -- VERY bad when mistaken as a string
- if ($token == '/')
- {
- $string_start_index = $in_regex - $removed;
- $string_length = ($location - $in_regex) + 1;
- $in_regex = false;
- }
- }
- else
- {
- // Make sure string hasn't been extracted by another operation...
- if (substr($str, $location - $removed, strlen($token)) != $token)
- {
- continue;
- }
-
- // This string shouldn't have been escaped...
- if ($location && $str[$location - $removed - 1] == '\\')
- {
- continue;
- }
-
- // See what this token is ...
- // Start of multi-line comment?
- if ($token == '/*')
- {
- $in_comment = $location;
- }
- // Start of a string?
- else if ($token == '"' || $token == "'")
- {
- $in_string = $token;
- $last_quote_pos = $location;
- }
- // A single-line comment?
- else if ($token == '//')
- {
- $comment_start_position = $location - $removed;
- $newline_pos = strpos($str, "\n", $comment_start_position);
- $comment_length = ($newline_pos !== false ? $newline_pos - $comment_start_position : $comment_start_position);
- $str = substr_replace($str, '', $comment_start_position, $comment_length);
- $removed += $comment_length;
- }
- // Start of a regex expression?
- // Note that the second part of this conditional fixes a bug: if there
- // is a regex sequence followed by a comment of the EXACT SAME length,
- // it will try to parse the regex sequence a second time...
- else if ($token == '/' && (!isset($quotes_and_comments[$location - 1]) || ($quotes_and_comments[$location - 1] != '//' && $quotes_and_comments[$location - 1] != '*/')))
- {
- // Only start a regex sequence if there was NOT
- // an alphanumeric sequence before.
- // var regex = /pattern/
- // string.match(/pattern/)
- if (preg_match('#[(=]#', $str[$location - $removed - 1]))
- {
- $in_regex = $location;
- }
- }
- }
- }
- }
- /**
- * Strips junk from code
- */
- function phpJSO_strip_junk (&$str, $whitespace_only = false)
- {
- // Remove unneeded spaces and semicolons
- $find = array
- (
- '/([^a-zA-Z0-9_$]|^)\s+([^a-zA-Z0-9_$]|$)/s', // Unneeded spaces between tokens
- '/([^a-zA-Z0-9_$]|^)\s+([a-zA-Z0-9_$]|$)/s', // Unneeded spaces between tokens
- '/([a-zA-Z0-9_$]|^)\s+([^a-zA-Z0-9_$]|$)/s', // Unneeded spaces between tokens
- '/([^a-zA-Z0-9_$]|^)\s+([^a-zA-Z0-9_$]|$)/s', // Unneeded spaces between tokens
- '/([^a-zA-Z0-9_$]|^)\s+([a-zA-Z0-9_$]|$)/s', // Unneeded spaces between tokens
- '/([a-zA-Z0-9_$]|^)\s+([^a-zA-Z0-9_$]|$)/s', // Unneeded spaces between tokens
- '/[\r\n]/s', // Unneeded newlines
- "/\t+/" // replace tabs with spaces
- );
- // Unneeded semicolons
- if (!$whitespace_only)
- {
- $find[] = '/;(\}|$)/si';
- }
- $replace = array
- (
- '$1$2',
- '$1$2',
- '$1$2',
- '$1$2',
- '$1$2',
- '$1$2',
- '',
- ' ',
- '$1',
- );
- $str = preg_replace($find, $replace, $str);
- }
- /**
- * Collapses code blocks.
- */
- function phpJSO_collapse_blocks ($code, &$collapse_count)
- {
-
- // The :parenthetical: is replaced dynamically in the loop below.
- // The key values mean this: the first and second values in the array are the indexes
- // of the parenthetical subscripts, and the third value is the replace value
- // for the regex.
- $regex = array
- (
- // When there is one command inside a block, remove brackets
- '#((if|for|while)\(:paren0:\))\{([^;{}]*;)\}#si' => array(3, 0, '$1$5', 5, 0),
- '#((if|for|while)\(:paren0:\))\{([^;{}]*)\}(?!;)#si' => array(3, 0, '$1$5;', 5, 0),
- '#((if|for|while)\(:paren0:\))\{([^;{}]*)\}(?=;)#si' => array(3, 0, '$1$5', 5, 0),
- // Collapse brackets with else and do statements
- '#(do|else)\{([^;{}]*)\}#si' => array(0, 0, '$1 $2;', 2, 0),
- '#(do|else)\{([^;{}]*;)\}#si' => array(0, 0, '$1 $2', 2, 0),
- // Remove brackets when a block is inside a block, EG if(1){if(2){}}
- '#((if|for|while)\(:paren0:\))\{((if|for|while|function [a-zA-Z_$][a-zA-Z0-9_$]*)\(:paren1:\))\{([^{}]*)\}\}(?!else)#si' => array(3, 7, '$1$5{$9}', 0, 0),
- '#((if|for|while)\(:paren0:\))\{((if|for|while|function [a-zA-Z_$][a-zA-Z0-9_$]*)\(:paren1:\))([^{};]*);?\}(?!else)#si' => array(3, 7, '$1$5$9;', 0, 0),
- '#((if|for|while)\(:paren0:\))\{([^;{]*)\{([^{}]*)\};?\}(?!else)#siU' => array(3, 0, '$1$5{$6};$7', 0, 0),
- // Remove brackets when a block is inside a block with no parentheticals, EG else{if(2){}}
- '#(else|do)\{((if|for|while|function [a-zA-Z_$][a-zA-Z0-9_$]*)\(:paren0:\))\{([^{}]*)\}\}#si' => array(4, 0, '$1 $2{$6}', 0, 0),
- '#(else|do)\{((if|for|while|function [a-zA-Z_$][a-zA-Z0-9_$]*)\(:paren0:\))([^{};]*);?\}#si' => array(4, 0, '$1 $2$6;', 0, 0),
- '#(else|do)\{([^;{}]*)\{([^{}]*)\};?\}#si' => array(0, 0, '$1 $2{$3};', 0, 0)
- );
- // Collapse all blocks when possible
- while (1)
- {
- $original_code = $code;
- // Loop through all patterns
- foreach ($regex as $find => $regex_data)
- {
- // Match all occurences of pattern
- $matches = array();
- $find_all = str_replace(':paren0:', '([^{}()]*(\([^{}]*)?)', $find);
- $find_all = str_replace(':paren1:', '([^{}()]*(\([^{}]*)?)', $find_all);
- preg_match_all($find_all, $code, $matches);
-
- // Loop through all matches, and if the number of opening and closing
- // parentheses is even, collapse the block
- for ($i = 0; isset($matches[0][$i]); ++$i)
- {
- // Don't find nested loops in some patterns
- if ($regex_data[3] && preg_match('#^if#si', $matches[$regex_data[3]][$i]))
- {
- continue;
- }
-
- // If loops are immediately followed by "else", don't continue
- if ($regex_data[4] && strtolower($matches[$regex_data[4]][$i]) == 'else')
- {
- continue;
- }
-
- $complete_match = true;
- $find_complete = $find;
- for ($j = 0; $j != 2; ++$j)
- {
- if ($regex_data[$j])
- {
- $parenthetical = &$matches[$regex_data[$j]][$i];
- if (!($parenthetical = phpJSO_is_valid_parenthetical($parenthetical)))
- {
- $complete_match = false;
- }
- $find_complete = str_replace(':paren'.$j.':', '((' . preg_quote($parenthetical) . '))', $find_complete);
- }
- }
- if ($complete_match)
- {
- $code = preg_replace($find_complete, $regex_data[2], $code);
- ++$collapse_count;
- }
- }
- }
- break;
- if ($original_code === $code)
- {
- break;
- }
- }
- return $code;
- }
- /**
- * Collapse math constants in code.
- */
- function phpJSO_collapse_math ($code, &$collapsed)
- {
- preg_match_all('#(^|[^a-zA-Z0-9_\$])(([()]|([\+\-\/\*\%])?(\-)?(0x[0-9a-fA-F]+|[0-9]+(\.[0-9]+)?))+)([^a-zA-Z0-9_\$]|$)#s', $code, $matches);
- // Loop through all matches
- for ($i = 0; isset($matches[0][$i]); ++$i)
- {
- $match = $matches[2][$i];
- // Make sure it is a valid math block
- if (!($match = phpJSO_is_valid_parenthetical($match)))
- {
- continue;
- }
- // Must end and begin with parentheses or numbers
- if ($match{0} != '(' && !is_numeric($match{0}))
- {
- continue;
- }
- $last_index = strlen($match) - 1;
- if ($match{$last_index} != ')' && !is_numeric($match{strlen($match) - 1}) && !ctype_alnum($match{$last_index}))
- {
- continue;
- }
- // Must be more than just symbols or just numbers
- //if (!preg_match('#[0-9]#', $match) || preg_match('#^[0-9]+$#', $match))
- //{
- // continue;
- //}
- if (preg_match('#\(\)#', $match))
- {
- continue;
- }
- // Convert hex to dec if the dec is smaller
- preg_match_all('#0x[0-9a-fA-F]+#', $code, $hex_matches);
- foreach ($hex_matches[0] as $hex_match)
- {
- $dec = hexdec($hex_match);
- if (strlen($dec) <= strlen($hex_match))
- {
- $code = str_replace($hex_match, $dec, $code);
- $match = str_replace($hex_match, $dec, $match);
- }
- }
- // Parse it, replace it
- $code = @preg_replace('#'.preg_quote($match).'#e', $match, $code);
- ++$collapsed;
- }
-
- return $code;
- }
- /**
- * Get all the tokens in code and put them in two arrays - one array
- * for just numeric tokens, and another array for all the rest.
- */
- function phpJSO_get_tokens ($code, &$numeric_tokens, &$tokens)
- {
- preg_match_all('#([a-zA-Z0-9\_\$]+)#s', $code, $match);
- $matched_tokens = array_values(array_unique($match[0]));
- phpJSO_count_duplicates($duplicates, $match[0]);
- foreach ($matched_tokens as $token)
- {
- // If token is an integer, we do replacements differently
- if (preg_match('#^([1-9][0-9]*|0)$#', $token))
- {
- $numeric_tokens[$token] = 1;
- }
- // We can place token in the array normally (but it's only worth doing
- // a replacement if the token isn't just one character).
- // It's also only worth doing a replacement if the token appears more than once in code.
- else if (isset($token{1}) && $duplicates[$token] > 1)
- {
- $tokens[] = $token;
- }
- }
- }
- /**
- * Merges the two token arrays: numeric tokens and regular tokens.
- * Specifically this function will take all the numeric tokens and
- * POSSIBLY put them in the token array if that's necessary.
- */
- function phpJSO_merge_token_arrays (&$tokens, &$numeric_tokens)
- {
- // Sort numeric token array
- ksort($numeric_tokens);
- // Loop through all numeric tokens
- $num_tokens = count($tokens);
- foreach ($numeric_tokens as $int=>$void)
- {
- if ($num_tokens < $int)
- {
- // We may not need to consider ANY more numeric tokens, if this
- // one is lower than the number of tokens, since the numeric tokens
- // are sorted already. This can potentially save a lot of time.
- if (strlen(strval($num_tokens)) >= strlen(strval($int)))
- {
- break;
- }
- else
- {
- $tokens[] = $int;
- continue;
- }
- }
- phpJSO_insert_token($tokens, '', $int);
- ++$num_tokens;
- }
- }
- /**
- * Inserts a token into the token array. Shifts all the other tokens
- * and puts it somewhere in the middle, based on token_index.
- */
- function phpJSO_insert_token (&$token_array, $token, $token_index)
- {
- // Loop through array and shift all indexes up one spot until we reach the
- // index we are inserting at
- $jump = 1;
- $token_index_count = $token_index - 1;
- for ($i = count($token_array) - 1; $i > $token_index_count; --$i)
- {
- if ($token_array[$i] == '')
- {
- ++$jump;
- continue;
- }
- $token_array[$i+$jump] = $token_array[$i];
- $jump = 1;
- }
- $token_array[$token_index] = $token;
- }
- /**
- * Place stripped strings back into code
- */
- function phpJSO_restore_strings (&$str, &$strings)
- {
- //do
- //{
- $str = preg_replace('#`([0-9]+)`#e', 'isset($strings[\'$1\']) ? $strings[\'$1\'] : \'`$1`\'', $str);
- //}
- //while (preg_match('#`([0-9]+)`#', $str));
- }
- /**
- * Count duplicate values in an array
- */
- function phpJSO_count_duplicates (&$dupes, $ary)
- {
- foreach ($ary as $v)
- {
- //$dupes[$v] = (isset($dupes[$v]) ? $dupes[$v] : 0) + 1;
- if (isset($dupes[$v]))
- {
- ++$dupes[$v];
- }
- else
- {
- $dupes[$v] = 1;
- }
- }
- }
- /**
- * Replaces tokens in code with the corresponding token index.
- */
- function phpJSO_replace_tokens (&$tokens, &$code)
- {
- $tokens_flipped = array_flip($tokens);
- unset($tokens_flipped['']);
- $find = '#\b('.implode('|', array_flip($tokens_flipped)).')\b#e';
- $code = preg_replace($find, '(isset($tokens_flipped[\'$1\']) ? $tokens_flipped[\'$1\'] : \'$1\')', $code);
- }
- /**
- * Check whether a parenthetical is valid or not.
- */
- function phpJSO_is_valid_parenthetical ($parenthetical)
- {
- $open_parentheses = 0;
-
- // Get all parentheses in the string
- $parentheses = phpJSO_sort_occurances($parenthetical, array('(', ')'));
- // Loop through parentheses
- foreach ($parentheses as $index => $parenthesis)
- {
- if ($parenthesis == ')')
- {
- if (!$open_parentheses)
- {
- return ($index ? substr($parenthetical, 0, $index) : false);
- }
- --$open_parentheses;
- }
- else
- {
- ++$open_parentheses;
- }
- }
- if ($open_parentheses != 0)
- {
- return false;
- }
- return $parenthetical;
- }
- /**
- * Finds all occurances of different strings in the first passed string and sorts
- * them by location. Returns array of locations. The key of each array element is the string
- * index (location) where the string was found; the value is the actual string, as seen below.
- *
- * [18] => "
- * [34] => "
- * [56] => /*
- * [100] => '
- */
- function phpJSO_sort_occurances (&$haystack, $needles)
- {
- $locations = array();
-
- foreach ($needles as $needle)
- {
- $pos = -1;
- //$needle_length = strlen($needle);
- while (($pos = @strpos($haystack, $needle, $pos+1)) !== false)
- {
- // Don't save location if string length is 1, and the needle is escaped
- if ($pos && $haystack[$pos - 1] == '\\' && $needle != '*/')
- {
- continue;
- }
- // Save location of needle
- $locations[$pos] = $needle;
- }
- }
-
- ksort($locations);
-
- return $locations;
- }
- /**
- * For timing compression
- */
- function phpJSO_microtime_float()
- {
- list($usec, $sec) = explode(" ", microtime());
- return ((float)$usec + (float)$sec);
- }
- ?>