Overview

Classes

  • Packager_Common_Base
  • Packager_Phar
  • Packager_Phar_ResultCompleter
  • Packager_Php
  • Packager_Php_Base
  • Packager_Php_Completer
  • Packager_Php_Scripts_Completer
  • Packager_Php_Scripts_Dependencies
  • Packager_Php_Scripts_Order
  • Packager_Php_Scripts_Replacer
  • Packager_Php_Wrapper
  • Packager_Php_Wrapper_DirectoryIterator
  • Packager_Php_Wrapper_SplFileInfo
  • Overview
  • Class
  • Tree
  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();
    }
    /* protected *************************************************************************************/
    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])) {
                    // if there is any part of php code for possible processing:
                    list ($i, $newPart) = $this->processPhpCodeReplacement(
                        $oldPart, $tokenId, $i
                    );
                } else {
                    // if there is not any part of php code for possible processing,
                    // just add php code part into result code string:
                    $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];
            // manage curly brackets (to determinate class closed moment to switch back $this->classState to "0")
            if ($this->classState > 0) {
                if ($tokenId === T_CURLY_OPEN || $tokenId === T_DOLLAR_OPEN_CURLY_BRACES) {
                    $this->classBracketsLevel += 1;
                }
            }
            // if token is "class" keyword - open stage
            if ($tokenId === T_CLASS) {
                $this->classState = 1;
                $this->classBracketsLevel = 0;
                // prepare bools for class methods
                $this->classFnDynamicEnvironment = FALSE;
                $this->classFnDynamicMonitorIndex = -1;
                $this->classFnStaticEnvironment = FALSE;
                $this->classFnStaticMonitorIndex = -1;
            }
        } else if (is_string($token)) {
            // manage curly brackets (to determinate class closed moment to switch back $this->classState to "0")
            if ($this->classState > 0) {
                if ($token === '{') {
                    if ($this->classState === 1) $this->classState = 2;
                    $this->classBracketsLevel += 1;
                } else if ($token === '}') {
                    $this->classBracketsLevel -= 1;
                }
            }
        }
        // determinate class closed moment - if class state is 2 and curly bracket counters are both 0
        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];
            // manage curly brackets (to determinate class closed moment to switch back $this->classState to "0")
            if ($functionsStatesLastRec > 0) {
                if ($tokenId === T_CURLY_OPEN || $tokenId === T_DOLLAR_OPEN_CURLY_BRACES) {
                    $this->functionsBracketsLevels[$monitorLastIndex] += 1;
                }
            }
            // if token is "function" keyword - open stage
            if ($tokenId === T_FUNCTION) {
                $monitorLastIndex += 1;
                $this->functionsStates[$monitorLastIndex] = 1;
                $this->functionsBracketsLevels[$monitorLastIndex] = 0;
                // determinate if we are in class dynamic function
                $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)) {
            // manage curly brackets (to determinate class closed moment to switch back $this->classState to "0")
            if ($functionsStatesLastRec > 0) {
                if ($token === '{') {
                    if ($functionsStatesLastRec === 1) $this->functionsStates[$monitorLastIndex] = 2;
                    $this->functionsBracketsLevels[$monitorLastIndex] += 1;
                } else if ($token === '}') {
                    $this->functionsBracketsLevels[$monitorLastIndex] -= 1;
                }
            }
        }
        // determinate class closed moment - if class state is 2 and curly bracket counters are both 0
        if ($this->functionsStates[$monitorLastIndex] === 2 && $this->functionsBracketsLevels[$monitorLastIndex] === 0) {
            // manage static vs dynamic class function booleans
            if ($this->classFnDynamicEnvironment && $this->classFnDynamicMonitorIndex === $monitorLastIndex) {
                $this->classFnDynamicEnvironment = FALSE;
                $this->classFnDynamicMonitorIndex = -1;
            }
            if ($this->classFnStaticEnvironment && $this->classFnStaticMonitorIndex === $monitorLastIndex) {
                
                $this->classFnStaticEnvironment = FALSE;
                $this->classFnStaticMonitorIndex = -1;
            }

            // manage states and brackets records
            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)) {
            // if there is configured item in replacements array in type: callable closure function,
            // there will be probably replacements for __FILE__ and __DIR__ replacements,
            // call these functions to process replacements
            $newPart = $replacement($this->fileInfo);
        } else {
            if (self::$phpFsMode == Packager_Php::FS_MODE_STRICT_HDD) {
                $newPart = $oldPart;
            } else {
                if (is_object($replacement)) {
                    // if we have current php function call between php replacements
                    list($i, $newPart) = $this->processPhpCodeReplacementObjectType(
                        $replacement, $oldPart, $tokenId, $i
                    );
                } else if (is_array($replacement)) {
                    // if there is configured item in replacements array in type: string,
                    // there will be probably replacements for requires and includes or any other php statement
                    // determinate if current statement is necessary to process by config
                    // and fill $newPart variable with proper content
                    list($i, $newPart) = $this->processPhpCodeReplacementArrayType(
                        $replacement, $oldPart, $tokenId, $i
                    );
                }
            }
        }
        return array($i, $newPart);
    }
    // php function calls - any_php_build_in_function() or programmerCustomFunction()
    // php class names, keywords, stdClass keys, TRUE/FALSE, public constants like PHP_EOL and other php shit...
    protected function processPhpCodeReplacementObjectType ($replacement, $oldPart, $tokenId, $i) {
        $newPart = '';
        if (isset($replacement->$oldPart)) {
            // determinate if current function call is necessary to process by config
            // and fill $newPart variable with proper content
            if (isset(self::$phpFunctionsToProcess[$oldPart])) {
                if ($this->startsWithUpper($oldPart)) {
                    // look into previous token and check if it is a backslash or not
                    $previousToken = isset($this->tokens[$i - 1]) ? $this->tokens[$i - 1] : array();
                    if (gettype($previousToken) == 'array' && $previousToken) {
                        $previousTokenVal = $previousToken[1];
                        if ($previousTokenVal == '\\') {
                            // remove last character added into result and continue
                            $resultLength = mb_strlen($this->result);
                            $this->result = mb_substr($this->result, 0, $resultLength - 1);
                        }
                    }
                }
                // yes - by configuration is necessary to replace this php function call - do it
                $newPart = str_replace('%WrapperClass%', self::$wrapperClassName, $replacement->$oldPart);
                self::addToReplacementStatistics($oldPart);
            } else {
                // no - keep original php function call
                $newPart = $oldPart;
            }
        } else {
            // it is other php statement - do not replace anything
            $newPart = $oldPart;
        }
        return array($i, $newPart);
    }
    // All occurrences of require_once(), include_once(), require() and include() statements replace by configuration.
    protected function processPhpCodeReplacementArrayType ($replacement, $oldPart, $tokenId, $i) {
        $newPart = '';
        if ($replacement[0] == $oldPart) {
            // determinate if current function call is necessary to process by config
            // and fill $newPart variable with proper content
            $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;
            }
            /*
            echo '<pre>';
            print_r($subTokens);
            echo '</pre>';
            */
            self::addToReplacementStatistics($oldPart);
        } else {
            // it is other php statement - do not replace anything
            $newPart = $oldPart;
        }
        return array($i, $newPart);
    }
    protected function getClassDynamicFunctionEnvironment ($functionIndex) {
        // go backwards and try to catch T_STATIC or not
        $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;
    }
}
MvcCore API documentation generated by ApiGen