Вход Регистрация
Файл: framework/core/manifest/ConfigStaticManifest.php
Строк: 377
<?php
/**
 * A utility class which builds a manifest of the statics defined in all classes, along with their
 * access levels and values
 *
 * We use this to make the statics that the Config system uses as default values be truely immutable.
 *
 * It has the side effect of allowing Config to avoid private-level access restrictions, so we can
 * optionally catch attempts to modify the config statics (otherwise the modification will appear
 * to work, but won't actually have any effect - the equvilent of failing silently)
 *
 * @package framework
 * @subpackage manifest
 */
class SS_ConfigStaticManifest {

    protected 
$base;
    protected 
$tests;

    protected 
$cache;
    protected 
$key;

    protected 
$index;
    protected 
$statics;

    static protected 
$initial_classes = array(
        
'Object''ViewableData''Injector''Director'
    
);

    
/**
     * Constructs and initialises a new config static manifest, either loading the data
     * from the cache or re-scanning for classes.
     *
     * @param string $base The manifest base path.
     * @param bool   $includeTests Include the contents of "tests" directories.
     * @param bool   $forceRegen Force the manifest to be regenerated.
     * @param bool   $cache If the manifest is regenerated, cache it.
     */
    
public function __construct($base$includeTests false$forceRegen false$cache true) {
        
$this->base  $base;
        
$this->tests $includeTests;

        
$cacheClass defined('SS_MANIFESTCACHE') ? SS_MANIFESTCACHE 'ManifestCache_File';

        
$this->cache = new $cacheClass('staticmanifest'.($includeTests '_tests' ''));
        
$this->key sha1($base);

        if(!
$forceRegen) {
            
$this->index $this->cache->load($this->key);
        }

        if(
$this->index) {
            
$this->statics $this->index['$statics'];
        }
        else {
            
$this->regenerate($cache);
        }
    }

    public function 
get($class$name$default) {
        if (!isset(
$this->statics[$class])) {
            if (isset(
$this->index[$class])) {
                
$info $this->index[$class];

                if (isset(
$info['key']) && $details $this->cache->load($this->key.'_'.$info['key'])) {
                    
$this->statics += $details;
                }

                if (!isset(
$this->statics[$class])) {
                    
$this->handleFile(null$info['path'], null);
                }
            }
            else {
                
$this->statics[$class] = false;
            }
        }

        if (isset(
$this->statics[$class][$name])) {
            
$static $this->statics[$class][$name];

            if (
$static['access'] != T_PRIVATE) {
                
Deprecation::notice('4.0'"Config static $class::$$name must be marked as private",
                    
Deprecation::SCOPE_GLOBAL);
                
// Don't warn more than once per static
                
$this->statics[$class][$name]['access'] = T_PRIVATE;
            }

            return 
$static['value'];
        }

        return 
$default;
    }

    
/**
     * Completely regenerates the manifest file.
     */
    
public function regenerate($cache true) {
        
$this->index = array('$statics' => array());
        
$this->statics = array();

        
$finder = new ManifestFileFinder();
        
$finder->setOptions(array(
            
'name_regex'    => '/^([^_].*.php)$/',
            
'ignore_files'  => array('index.php''main.php''cli-script.php''SSTemplateParser.php'),
            
'ignore_tests'  => !$this->tests,
            
'file_callback' => array($this'handleFile')
        ));

        
$finder->find($this->base);

        if(
$cache) {
            
$keysets = array();

            foreach (
$this->statics as $class => $details) {
                if (
in_array($classself::$initial_classes)) {
                    
$this->index['$statics'][$class] = $details;
                }
                else {
                    
$key sha1($class);
                    
$this->index[$class]['key'] = $key;

                    
$keysets[$key][$class] = $details;
                }
            }

            foreach (
$keysets as $key => $details) {
                
$this->cache->save($details$this->key.'_'.$key);
            }

            
$this->cache->save($this->index$this->key);
        }
    }

    public function 
handleFile($basename$pathname$depth) {
        
$parser = new SS_ConfigStaticManifest_Parser($pathname);
        
$parser->parse();

        
$this->index array_merge($this->index$parser->getInfo());
        
$this->statics array_merge($this->statics$parser->getStatics());
    }

    public function 
getStatics() {
        return 
$this->statics;
    }
}

/**
 * A parser that processes a PHP file, using PHP's built in parser to get a string of tokens,
 * then processing them to find the static class variables, their access levels & values
 *
 * We can't do this using TokenisedRegularExpression because we need to keep track of state
 * as we process the token list (when we enter and leave a namespace or class, when we see
 * an access level keyword, etc)
 *
 * @package framework
 * @subpackage manifest
 */
class SS_ConfigStaticManifest_Parser {

    protected 
$info = array();
    protected 
$statics = array();

    protected 
$path;
    protected 
$tokens;
    protected 
$length;
    protected 
$pos;

    function 
__construct($path) {
        
$this->path $path;
        
$file file_get_contents($path);

        
$this->tokens token_get_all($file);
        
$this->length count($this->tokens);
        
$this->pos 0;
    }

    function 
getInfo() {
        return 
$this->info;
    }

    function 
getStatics() {
        return 
$this->statics;
    }

    
/**
     * Get the next token to process, incrementing the pointer
     *
     * @param bool $ignoreWhitespace - if true will skip any whitespace tokens & only return non-whitespace ones
     * @return null | mixed - Either the next token or null if there isn't one
     */
    
protected function next($ignoreWhitespace true) {
        do {
            if(
$this->pos >= $this->length) return null;
            
$next $this->tokens[$this->pos++];
        }
        while(
$ignoreWhitespace && is_array($next) && $next[0] == T_WHITESPACE);

        return 
$next;
    }

    
/**
     * Get the next set of tokens that form a string to process,
     * incrementing the pointer
     *
     * @param bool $ignoreWhitespace - if true will skip any whitespace tokens
     *             & only return non-whitespace ones
     * @return null|string - Either the next string or null if there isn't one
     */
    
protected function nextString($ignoreWhitespace true) {
        static 
$stop = array('{''}''('')''['']');

        
$string '';
        while (
$this->pos $this->length) {
            
$next $this->tokens[$this->pos];
            if (
is_string($next)) {
                if (!
in_array($next$stop)) {
                    
$string .= $next;
                } else {
                    break;
                }
            } else if (
$next[0] == T_STRING) {
                
$string .= $next[1];
            } else if (
$next[0] != T_WHITESPACE || !$ignoreWhitespace) {
                break;
            }
            
$this->pos++;
        }
        if (
$string === '') {
            return 
null;
        } else {
            return 
$string;
        }
    }

    
/**
     * Parse the given file to find the static variables declared in it, along with their access & values
     */
    
function parse() {
        
$depth 0$namespace null$class null$clsdepth null$access 0;

        while(
$token $this->next()) {
            
$type = ($token === (array)$token) ? $token[0] : $token;

            if(
$type == T_CLASS) {
                
$next $this->nextString();
                if(
$next === null) {
                    
user_error("Couldn't parse {$this->path} when building config static manifest"E_USER_ERROR);
                }

                
$class $next;
            }
            else if(
$type == T_NAMESPACE) {
                
$namespace '';
                while(
true) {
                    
$next $this->next();

                    if(
$next == ';') {
                        break;
                    } elseif(
$next[0] == T_NS_SEPARATOR) {
                        
$namespace .= $next[1];
                        
$next $this->next();
                    }

                    if(!
is_string($next) && $next[0] != T_STRING) {
                        
user_error("Couldn't parse {$this->path} when building config static manifest"E_USER_ERROR);
                    }

                    
$namespace .= is_string($next) ? $next $next[1];
                }
            }
            else if(
$type == '{' || $type == T_CURLY_OPEN || $type == T_DOLLAR_OPEN_CURLY_BRACES){
                
$depth += 1;
                if(
$class && !$clsdepth$clsdepth $depth;
            }
            else if(
$type == '}') {
                
$depth -= 1;
                if(
$depth $clsdepth$class $clsdepth null;
                if(
$depth 0user_error("Hmm - depth calc wrong, hit negatives, see: ".$this->pathE_USER_ERROR);
            }
            else if(
$type == T_PUBLIC || $type == T_PRIVATE || $type == T_PROTECTED) {
                
$access $type;
            }
            else if(
$type == T_STATIC && $class && $depth == $clsdepth) {
                
$this->parseStatic($access$namespace $namespace.'\'.$class : $class);
                $access = 0;
            }
            else {
                $access = 0;
            }
        }
    }

    /**
     * During parsing we'
ve found a "static" keywordParse out the variable names and value
     
assignments that follow.
     *
     * 
Seperated out from parse partially so that we can recurse if there are multiple statics
     
being declared in a comma seperated list
     */
    function 
parseStatic($access$class) {
        
$variable null;
        
$value '';

        while(
$token $this->next()) {
            
$type = ($token === (array)$token) ? $token[0] : $token;

            if(
$type == T_PUBLIC || $type == T_PRIVATE || $type == T_PROTECTED) {
                
$access $type;
            }
            else if(
$type == T_FUNCTION) {
                return;
            }
            else if(
$type == T_VARIABLE) {
                
$variable substr($token[1], 1); // Cut off initial "$"
            
}
            else if(
$type == ';' || $type == ',' || $type == '=') {
                break;
            }
            else if(
$type == T_COMMENT || $type == T_DOC_COMMENT) {
                
// NOP
            
}
            else {
                
user_error('Unexpected token ("' token_name($type) . '") when building static manifest in class "'
                    
$class '": '.print_r($tokentrue), E_USER_ERROR);
            }
        }

        if(
$token == '=') {
            
$depth 0;

            while(
$token = ($this->pos >= $this->length) ? null $this->tokens[$this->pos++]) {
                
$type = ($token === (array)$token) ? $token[0] : $token;

                
// Track array nesting depth
                
if($type == T_ARRAY || $type == '[') {
                    
$depth += 1;
                } elseif(
$type == ')' || $type == ']') {
                    
$depth -= 1;
                }

                
// Parse out the assignment side of a static declaration,
                // ending on either a ';' or a ',' outside an array
                
if($type == T_WHITESPACE) {
                    
$value .= ' ';
                }
                else if(
$type == ';' || ($type == ',' && !$depth)) {
                    break;
                }
                
// Statics can reference class constants with self:: (and that won't work in eval)
                
else if($type == T_STRING && $token[1] == 'self') {
                    
$value .= $class;
                }
                else {
                    
$value .= ($token === (array)$token) ? $token[1] : $token;
                }
            }
        }

        if (!isset(
$this->info[$class])) {
            
$this->info[$class] = array(
                
'path' => $this->path,
                
'mtime' => filemtime($this->path),
            );
        }

        if(!isset(
$this->statics[$class])) {
            
$this->statics[$class] = array();
        }

        
$value trim($value);
        if (
$value) {
            
$value = eval('static $temp = '.$value.";n".'return $temp'.";n");
        }
        else {
            
$value null;
        }

        
$this->statics[$class][$variable] = array(
            
'access' => $access,
            
'value' => $value
        
);

        if(
$token == ','$this->parseStatic($access$class);
    }
}
Онлайн: 0
Реклама