1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414:
<?php
class Packager_Php_Scripts_Replacer
{
protected static $dynamicClassFnDeterminators = array(
'continueTokens'=> array(
T_WHITESPACE=> 1,
T_COMMENT => 1,
),
'endingTokens' => array(
T_PUBLIC => 1,
T_PROTECTED => 1,
T_PRIVATE => 1,
T_ABSTRACT => 1,
T_FINAL => 1,
),
);
protected static $phpFunctionsToProcess = array();
protected static $wrapperReplacements = array();
protected static $phpReplacementsStatistics = array();
protected static $wrapperClassName = '';
protected static $phpFsMode = '';
protected $cfg;
protected $result = '';
protected $scriptContent = '';
protected $statementEndOperator = ';';
protected $fileInfo = NULL;
protected $tokens = array();
protected $namespaceState = 0;
protected $classState = 0;
protected $classBracketsLevel = 0;
protected $functionsStates = array(0);
protected $functionsOpenIndexes = array(0);
protected $functionsBracketsLevels = array(0);
public static function SetPhpFunctionsToProcess ($phpFunctionsToProcess = array()) {
self::$phpFunctionsToProcess = $phpFunctionsToProcess;
}
public static function SetWrapperReplacements ($wrapperReplacements = array()) {
self::$wrapperReplacements = $wrapperReplacements;
}
public static function SetWrapperClassName ($wrapperClassName = '') {
self::$wrapperClassName = $wrapperClassName;
}
public static function SetPhpFsMode ($phpFsMode = '') {
self::$phpFsMode = $phpFsMode;
}
public static function GetReplacementsStatistics () {
return self::$phpReplacementsStatistics;
}
public static function ProcessReplacements (& $fileInfo, & $cfg) {
$instance = new self($fileInfo, $cfg, token_get_all($fileInfo->content));
return $instance->runReplacementsProcessing();
}
public static function ProcessNamespaces (& $fileInfo, & $cfg) {
$instance = new self($fileInfo, $cfg, token_get_all("<"."?php\n".$fileInfo->content));
return $instance->runNamespacesProcessing();
}
public function __construct(& $fileInfo, $cfg, $tokens) {
$this->fileInfo = & $fileInfo;
$this->cfg = & $cfg;
$this->tokens = & $tokens;
$this->result = '';
$this->classState = 0;
$this->classBracketsLevel = 0;
$this->functionsStates = array(0);
$this->functionsBracketsLevels = array(0);
$this->classFnDynamicEnvironment = FALSE;
$this->classFnDynamicMonitorIndex = -1;
$this->classFnStaticEnvironment = FALSE;
$this->classFnStaticMonitorIndex = -1;
}
protected function runReplacementsProcessing () {
$newPart = '';
for ($i = 0, $l = count($this->tokens); $i < $l;) {
$token = $this->tokens[$i];
if (is_array($token)) {
$tokenId = $token[0];
$oldPart = $token[1];
if (isset(self::$wrapperReplacements[$tokenId])) {
list ($i, $newPart) = $this->processPhpCodeReplacement(
$oldPart, $tokenId, $i
);
} else {
$newPart = $oldPart;
}
} else if (is_string($token)) {
if ($token == '(') {
$this->statementEndOperator = ')';
} else if ($token == ')') {
$this->statementEndOperator = ';';
}
$newPart = $token;
}
$this->monitorNamespace($token);
$this->monitorClass($token, $i);
$this->monitorFunctions($token, $i);
$this->result .= $newPart;
$i += 1;
}
return $this->result;
}
protected function runNamespacesProcessing () {
$newPart = '';
for ($i = 0, $l = count($this->tokens); $i < $l; $i += 1) {
$token = & $this->tokens[$i];
if (is_array($token)) {
$tokenId = $token[0];
$newPart = $token[1];
$token[3] = token_name($tokenId);
if ($tokenId == T_NAMESPACE) {
if ($this->namespaceState > 0) {
$this->result .= (!$this->cfg->minifyPhp ? "\n}\n" : '}');
}
$this->namespaceState = 1;
}
if ($tokenId == T_OPEN_TAG) $newPart = '';
} else if (is_string($token)) {
$newPart = $token;
if ($this->namespaceState == 1 && $token == ';') {
$newPart = '{';
$this->namespaceState = 2;
}
}
$this->result .= $newPart;
}
if ($this->namespaceState == 2) {
$this->result .= (!$this->cfg->minifyPhp ? "\n}" : '}');
}
return $this->result;
}
protected function monitorNamespace ($token) {
if ($this->namespaceState > 2 || $this->fileInfo->extension !== 'php') return;
if (is_array($token)) {
$tokenId = $token[0];
if ($this->namespaceState == 1 && $tokenId == T_STRING) {
$this->namespaceState = 2;
} else if ($tokenId == T_NAMESPACE) {
$this->namespaceState = 1;
}
} else if (is_string($token)) {
if ($this->namespaceState == 1) {
if ($token == '{') {
$this->fileInfo->containsNamespace = Packager_Php::NAMESPACE_GLOBAL_CURLY_BRACKETS;
$this->namespaceState = 3;
}
} else if ($this->namespaceState == 2) {
if ($token == ';') {
$this->fileInfo->containsNamespace = Packager_Php::NAMESPACE_NAMED_SEMICOLONS;
$this->namespaceState = 3;
} else if ($token == '{') {
$this->fileInfo->containsNamespace = Packager_Php::NAMESPACE_NAMED_CURLY_BRACKETS;
$this->namespaceState = 3;
}
}
}
}
protected function monitorClass ($token, $currentIndex) {
if (is_array($token)) {
$tokenId = $token[0];
if ($this->classState > 0) {
if ($tokenId === T_CURLY_OPEN || $tokenId === T_DOLLAR_OPEN_CURLY_BRACES) {
$this->classBracketsLevel += 1;
}
}
if ($tokenId === T_CLASS) {
$this->classState = 1;
$this->classBracketsLevel = 0;
$this->classFnDynamicEnvironment = FALSE;
$this->classFnDynamicMonitorIndex = -1;
$this->classFnStaticEnvironment = FALSE;
$this->classFnStaticMonitorIndex = -1;
}
} else if (is_string($token)) {
if ($this->classState > 0) {
if ($token === '{') {
if ($this->classState === 1) $this->classState = 2;
$this->classBracketsLevel += 1;
} else if ($token === '}') {
$this->classBracketsLevel -= 1;
}
}
}
if ($this->classState === 2 && $this->classBracketsLevel === 0) {
$this->classState = 0;
}
}
protected function monitorFunctions ($token, $currentIndex) {
$monitorLastIndex = count($this->functionsStates) - 1;
$functionsStatesLastRec = $this->functionsStates[$monitorLastIndex];
if (is_array($token)) {
$tokenId = $token[0];
if ($functionsStatesLastRec > 0) {
if ($tokenId === T_CURLY_OPEN || $tokenId === T_DOLLAR_OPEN_CURLY_BRACES) {
$this->functionsBracketsLevels[$monitorLastIndex] += 1;
}
}
if ($tokenId === T_FUNCTION) {
$monitorLastIndex += 1;
$this->functionsStates[$monitorLastIndex] = 1;
$this->functionsBracketsLevels[$monitorLastIndex] = 0;
$dynamicClassFn = FALSE;
if ($this->classState > 0) {
if (!$this->classFnStaticEnvironment && !$this->classFnDynamicEnvironment) {
$dynamicClassFn = $this->getClassDynamicFunctionEnvironment($currentIndex);
if ($dynamicClassFn) {
$this->classFnDynamicEnvironment = TRUE;
$this->classFnDynamicMonitorIndex = $monitorLastIndex;
} else {
$this->classFnStaticEnvironment = TRUE;
$this->classFnStaticMonitorIndex = $monitorLastIndex;
}
}
} else {
$this->classFnDynamicEnvironment = FALSE;
$this->classFnDynamicMonitorIndex = -1;
$this->classFnStaticEnvironment = FALSE;
$this->classFnStaticMonitorIndex = -1;
}
}
} else if (is_string($token)) {
if ($functionsStatesLastRec > 0) {
if ($token === '{') {
if ($functionsStatesLastRec === 1) $this->functionsStates[$monitorLastIndex] = 2;
$this->functionsBracketsLevels[$monitorLastIndex] += 1;
} else if ($token === '}') {
$this->functionsBracketsLevels[$monitorLastIndex] -= 1;
}
}
}
if ($this->functionsStates[$monitorLastIndex] === 2 && $this->functionsBracketsLevels[$monitorLastIndex] === 0) {
if ($this->classFnDynamicEnvironment && $this->classFnDynamicMonitorIndex === $monitorLastIndex) {
$this->classFnDynamicEnvironment = FALSE;
$this->classFnDynamicMonitorIndex = -1;
}
if ($this->classFnStaticEnvironment && $this->classFnStaticMonitorIndex === $monitorLastIndex) {
$this->classFnStaticEnvironment = FALSE;
$this->classFnStaticMonitorIndex = -1;
}
if ($monitorLastIndex > 0) {
unset($this->functionsStates[$monitorLastIndex]);
unset($this->functionsBracketsLevels[$monitorLastIndex]);
} else {
$this->functionsStates[0] = 0;
$this->functionsBracketsLevels[0] = 0;
}
}
}
protected function processPhpCodeReplacement ($oldPart, $tokenId, $i) {
$newPart = '';
$replacement = self::$wrapperReplacements[$tokenId];
if (is_callable($replacement)) {
$newPart = $replacement($this->fileInfo);
} else {
if (self::$phpFsMode == Packager_Php::FS_MODE_STRICT_HDD) {
$newPart = $oldPart;
} else {
if (is_object($replacement)) {
list($i, $newPart) = $this->processPhpCodeReplacementObjectType(
$replacement, $oldPart, $tokenId, $i
);
} else if (is_array($replacement)) {
list($i, $newPart) = $this->processPhpCodeReplacementArrayType(
$replacement, $oldPart, $tokenId, $i
);
}
}
}
return array($i, $newPart);
}
protected function processPhpCodeReplacementObjectType ($replacement, $oldPart, $tokenId, $i) {
$newPart = '';
if (isset($replacement->$oldPart)) {
if (isset(self::$phpFunctionsToProcess[$oldPart])) {
if ($this->startsWithUpper($oldPart)) {
$previousToken = isset($this->tokens[$i - 1]) ? $this->tokens[$i - 1] : array();
if (gettype($previousToken) == 'array' && $previousToken) {
$previousTokenVal = $previousToken[1];
if ($previousTokenVal == '\\') {
$resultLength = mb_strlen($this->result);
$this->result = mb_substr($this->result, 0, $resultLength - 1);
}
}
}
$newPart = str_replace('%WrapperClass%', self::$wrapperClassName, $replacement->$oldPart);
self::addToReplacementStatistics($oldPart);
} else {
$newPart = $oldPart;
}
} else {
$newPart = $oldPart;
}
return array($i, $newPart);
}
protected function processPhpCodeReplacementArrayType ($replacement, $oldPart, $tokenId, $i) {
$newPart = '';
if ($replacement[0] == $oldPart) {
$newPart = str_replace('%WrapperClass%', self::$wrapperClassName, $replacement[1]) . '(';
$subTokens = array();
$j = $i + 1;
while (TRUE) {
$subToken = & $this->tokens[$j];
if (is_array($subToken)) {
$subTokenId = $subToken[0];
$subToken[3] = token_name($subTokenId);
$subTokens[] = & $subToken;
} else if (is_string($subToken)) {
if ($subToken == $this->statementEndOperator) {
$subInstance = new self($this->fileInfo, $this->cfg, $subTokens);
$newSubPart = $subInstance->runReplacementsProcessing();
if ($this->classFnDynamicEnvironment) {
$newPart .= $newSubPart . ', $this)' . $this->statementEndOperator;
} else {
$newPart .= $newSubPart . ')' . $this->statementEndOperator;
}
$i = $j;
break;
} else {
$subTokens[] = $subToken;
}
}
$j += 1;
}
self::addToReplacementStatistics($oldPart);
} else {
$newPart = $oldPart;
}
return array($i, $newPart);
}
protected function getClassDynamicFunctionEnvironment ($functionIndex) {
$staticCatched = FALSE;
$continueTokens = self::$dynamicClassFnDeterminators['continueTokens'];
$endingTokens = self::$dynamicClassFnDeterminators['endingTokens'];
for ($i = $functionIndex - 1; $i > -1; $i -= 1) {
$token = $this->tokens[$i];
if (is_array($token)) {
$tokenId = $token[0];
if (isset($continueTokens[$tokenId])) {
continue;
} else if (isset($endingTokens[$tokenId])) {
break;
} else if ($tokenId == T_STATIC) {
$staticCatched = TRUE;
break;
} else {
break;
}
} else if (is_string($token)) {
if ($token == '&') {
continue;
} else {
break;
}
}
}
return $staticCatched ? FALSE : TRUE;
}
protected function startsWithUpper ($str) {
$chr = mb_substr ($str, 0, 1, "UTF-8");
return mb_strtolower($chr, "UTF-8") != $chr;
}
protected static function addToReplacementStatistics ($replacementKey) {
if (!isset(self::$phpReplacementsStatistics[$replacementKey])) {
self::$phpReplacementsStatistics[$replacementKey] = 0;
}
self::$phpReplacementsStatistics[$replacementKey] += 1;
}
}