mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #4148 from micmania1/replace-static-manifest
NEW Update SS_ConfigStaticManifest to use Reflection
This commit is contained in:
commit
7fa97c4d9e
@ -112,7 +112,7 @@ if(file_exists(BASE_PATH . '/vendor/autoload.php')) {
|
||||
}
|
||||
|
||||
// Now that the class manifest is up, load the static configuration
|
||||
$configManifest = new SS_ConfigStaticManifest(BASE_PATH, false, $flush);
|
||||
$configManifest = new SS_ConfigStaticManifest();
|
||||
Config::inst()->pushConfigStaticManifest($configManifest);
|
||||
|
||||
// And then the yaml configuration
|
||||
|
@ -1,385 +1,42 @@
|
||||
<?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)
|
||||
* Allows access to config values set on classes using private statics.
|
||||
*
|
||||
* @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 $class
|
||||
* @param string $name
|
||||
* @param null $default
|
||||
*
|
||||
* @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.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __construct($base, $includeTests = false, $forceRegen = false, $cache = true) {
|
||||
$this->base = $base;
|
||||
$this->tests = $includeTests;
|
||||
public function get($class, $name, $default = null) {
|
||||
if(class_exists($class)) {
|
||||
|
||||
$cacheClass = defined('SS_MANIFESTCACHE') ? SS_MANIFESTCACHE : 'ManifestCache_File';
|
||||
// The config system is case-sensitive so we need to check the exact value
|
||||
$reflection = new ReflectionClass($class);
|
||||
if(strcmp($reflection->name, $class) === 0) {
|
||||
|
||||
$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($reflection->hasProperty($name)) {
|
||||
$property = $reflection->getProperty($name);
|
||||
if($property->isStatic()) {
|
||||
if(!$property->isPrivate()) {
|
||||
Deprecation::notice('4.0', "Config static $class::\$$name must be marked as private",
|
||||
Deprecation::SCOPE_GLOBAL);
|
||||
return null;
|
||||
}
|
||||
$property->setAccessible(true);
|
||||
return $property->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
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($class, self::$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 = is_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 < 0) user_error("Hmm - depth calc wrong, hit negatives, see: ".$this->path, E_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" keyword. Parse 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 = is_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($token, true), E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if($token == '=') {
|
||||
$depth = 0;
|
||||
|
||||
while($token = $this->next(false)){
|
||||
$type = is_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 .= is_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);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -225,6 +225,8 @@ class DataQuery {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Resolve colliding fields
|
||||
if($this->collidingFields) {
|
||||
foreach($this->collidingFields as $k => $collisions) {
|
||||
|
@ -2,268 +2,31 @@
|
||||
|
||||
class ConfigStaticManifestTest extends SapphireTest {
|
||||
|
||||
/* Example statics */
|
||||
private static $testString = 'string';
|
||||
|
||||
// Different access levels
|
||||
static $nolevel;
|
||||
public static $public;
|
||||
protected static $protected;
|
||||
private static $private;
|
||||
static public $public2;
|
||||
static protected $protected2;
|
||||
static private $private2;
|
||||
static $nolevel_after_private;
|
||||
private static $testArray = array('foo' => 'bar');
|
||||
|
||||
// Assigning values
|
||||
static $snone;
|
||||
static $snull = null;
|
||||
static $sint = 1;
|
||||
static $sfloat = 2.5;
|
||||
static $sstring = 'string';
|
||||
static $sarray = array(1, 2, array(3, 4), 5);
|
||||
static $sheredoc = <<<DOC
|
||||
heredoc
|
||||
DOC;
|
||||
static $snowdoc = <<<'DOC'
|
||||
nowdoc
|
||||
DOC;
|
||||
protected static $ignored = true;
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
// Assigning multiple values
|
||||
static $onone, $onull = null, $oint = 1, $ofloat = 2.5, $ostring = 'string', $oarray = array(1, 2, array(3, 4), 5), $oheredoc = <<<DOC
|
||||
heredoc
|
||||
DOC
|
||||
, $onowdoc = <<<'DOC'
|
||||
nowdoc
|
||||
DOC;
|
||||
// @codingStandardsIgnoreEnd
|
||||
public function testGet() {
|
||||
$manifest = new SS_ConfigStaticManifest();
|
||||
|
||||
static
|
||||
$mnone,
|
||||
$mnull = null,
|
||||
$mint = 1,
|
||||
$mfloat = 2.5,
|
||||
$mstring = 'string',
|
||||
$marray = array(
|
||||
1, 2,
|
||||
array(3, 4),
|
||||
5
|
||||
),
|
||||
$mheredoc = <<<DOC
|
||||
heredoc
|
||||
DOC
|
||||
,
|
||||
$mnowdoc = <<<'DOC'
|
||||
nowdoc
|
||||
DOC;
|
||||
// Test madeup value
|
||||
$this->assertNull($manifest->get(__CLASS__, 'madeup', null));
|
||||
|
||||
// Test string value
|
||||
$this->assertEquals('string', $manifest->get(__CLASS__, 'testString'));
|
||||
|
||||
static /* Has comment inline */ $commented_int = 1, /* And here */ $commented_string = 'string';
|
||||
// Test array value
|
||||
$this->assertEquals(array('foo' => 'bar'), $manifest->get(__CLASS__, 'testArray'));
|
||||
|
||||
static
|
||||
/**
|
||||
* Has docblock inline
|
||||
*/
|
||||
$docblocked_int = 1,
|
||||
/** And here */
|
||||
$docblocked_string = 'string';
|
||||
// Test to ensure we're only picking up private statics
|
||||
$this->assertNull($manifest->get(__CLASS__, 'ignored', null));
|
||||
|
||||
// Should ignore static methpds
|
||||
static function static_method() {}
|
||||
|
||||
// Should ignore method statics
|
||||
function instanceMethod() {
|
||||
static $method_static;
|
||||
}
|
||||
|
||||
/* The tests */
|
||||
|
||||
protected function parseSelf() {
|
||||
static $statics = null;
|
||||
|
||||
if ($statics === null) {
|
||||
$parser = new SS_ConfigStaticManifest_Parser(__FILE__);
|
||||
$parser->parse();
|
||||
}
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function testParsingAccessLevels() {
|
||||
$statics = $this->parseSelf()->getStatics();
|
||||
|
||||
$levels = array(
|
||||
'nolevel' => null,
|
||||
'public' => T_PUBLIC,
|
||||
'public2' => T_PUBLIC,
|
||||
'protected' => T_PROTECTED,
|
||||
'protected2' => T_PROTECTED,
|
||||
'private' => T_PRIVATE,
|
||||
'private2' => T_PRIVATE,
|
||||
'nolevel_after_private' => null
|
||||
);
|
||||
|
||||
foreach($levels as $var => $level) {
|
||||
$this->assertEquals(
|
||||
$level,
|
||||
$statics[__CLASS__][$var]['access'],
|
||||
'Variable '.$var.' has '.($level ? token_name($level) : 'no').' access level'
|
||||
);
|
||||
// Test madeup class
|
||||
if(!class_exists('aonsffgrgx')) {
|
||||
$this->assertNull($manifest->get('aonsffgrgx', 'madeup', null));
|
||||
}
|
||||
}
|
||||
|
||||
public function testParsingValues() {
|
||||
$statics = $this->parseSelf()->getStatics();
|
||||
|
||||
// Check assigning values
|
||||
$values = array(
|
||||
'none',
|
||||
'null',
|
||||
'int',
|
||||
'float',
|
||||
'string',
|
||||
'array',
|
||||
'heredoc',
|
||||
'nowdoc'
|
||||
);
|
||||
|
||||
$prepends = array(
|
||||
's', // Each on it's own
|
||||
'o', // All on one line
|
||||
'm' // All in on static statement, but each on own line
|
||||
);
|
||||
|
||||
foreach ($values as $value) {
|
||||
foreach ($prepends as $prepend) {
|
||||
$var = "$prepend$value";
|
||||
|
||||
$this->assertEquals(
|
||||
self::$$var,
|
||||
$statics[__CLASS__][$var]['value'],
|
||||
'Variable '.$var.' value is extracted properly'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testIgnoreComments() {
|
||||
$statics = $this->parseSelf()->getStatics();
|
||||
|
||||
$this->assertEquals(self::$commented_int, $statics[__CLASS__]['commented_int']['value']);
|
||||
$this->assertEquals(self::$commented_string, $statics[__CLASS__]['commented_string']['value']);
|
||||
|
||||
$this->assertEquals(self::$docblocked_int, $statics[__CLASS__]['docblocked_int']['value']);
|
||||
$this->assertEquals(self::$docblocked_string, $statics[__CLASS__]['docblocked_string']['value']);
|
||||
}
|
||||
|
||||
public function testIgnoresMethodStatics() {
|
||||
$statics = $this->parseSelf()->getStatics();
|
||||
$this->assertNull(@$statics[__CLASS__]['method_static']);
|
||||
}
|
||||
|
||||
public function testIgnoresStaticMethods() {
|
||||
$statics = $this->parseSelf()->getStatics();
|
||||
$this->assertNull(@$statics[__CLASS__]['static_method']);
|
||||
}
|
||||
|
||||
public function testParsingShortArray() {
|
||||
if(version_compare(PHP_VERSION, '5.4', '<')) {
|
||||
$this->markTestSkipped('This test requires PHP 5.4 or higher');
|
||||
return;
|
||||
}
|
||||
|
||||
$parser = new SS_ConfigStaticManifest_Parser(__DIR__ .
|
||||
'/ConfigStaticManifestTest/ConfigStaticManifestTestMyObject.php');
|
||||
$parser->parse();
|
||||
|
||||
$statics = $parser->getStatics();
|
||||
|
||||
$expectedValue = array(
|
||||
'Name' => 'Varchar',
|
||||
'Description' => 'Text',
|
||||
);
|
||||
|
||||
$this->assertEquals($expectedValue, $statics['ConfigStaticManifestTestMyObject']['db']['value']);
|
||||
}
|
||||
|
||||
public function testParsingNamespacesclass() {
|
||||
$parser = new SS_ConfigStaticManifest_Parser(__DIR__ .
|
||||
'/ConfigStaticManifestTest/ConfigStaticManifestTestNamespace.php');
|
||||
$parser->parse();
|
||||
|
||||
$statics = $parser->getStatics();
|
||||
|
||||
$expectedValue = array(
|
||||
'Name' => 'Varchar',
|
||||
'Description' => 'Text',
|
||||
);
|
||||
|
||||
$this->assertEquals($expectedValue, $statics['config\staticmanifest\NamespaceTest']['db']['value']);
|
||||
}
|
||||
|
||||
public function testParsingMultyStringClass() {
|
||||
static $tokens = array(
|
||||
array(T_OPEN_TAG, "<?php\n", 1),
|
||||
array(T_WHITESPACE, "\n", 2),
|
||||
array(T_CLASS, 'class', 3),
|
||||
array(T_WHITESPACE, ' ', 3),
|
||||
':',
|
||||
array(T_STRING, 'ss', 3),
|
||||
':',
|
||||
array(T_STRING, 'test2', 3),
|
||||
array(T_WHITESPACE, ' ', 3),
|
||||
array(T_EXTENDS, 'extends', 3),
|
||||
array(T_WHITESPACE, ' ', 3),
|
||||
':',
|
||||
array(T_STRING, 'ss', 3),
|
||||
':',
|
||||
array(T_STRING, 'test', 3),
|
||||
array(T_WHITESPACE, ' ', 3),
|
||||
array(T_IMPLEMENTS, 'implements', 3),
|
||||
array(T_WHITESPACE, ' ', 3),
|
||||
array(T_STRING, 'TestOnly', 3),
|
||||
array(T_WHITESPACE, ' ', 3),
|
||||
'{',
|
||||
array(T_WHITESPACE, "\n\t", 3),
|
||||
array(T_PRIVATE, 'private', 4),
|
||||
array(T_WHITESPACE, ' ', 4),
|
||||
array(T_STATIC, 'static', 4),
|
||||
array(T_WHITESPACE, ' ', 4),
|
||||
array(T_VARIABLE, '$test', 4),
|
||||
array(T_WHITESPACE, ' ', 4),
|
||||
'=',
|
||||
array(T_WHITESPACE, ' ', 4),
|
||||
array(T_ARRAY, 'array', 4),
|
||||
'(',
|
||||
array(T_LNUMBER, '3', 4),
|
||||
')',
|
||||
';',
|
||||
array(T_WHITESPACE, "\n", 4),
|
||||
'}',
|
||||
array(T_WHITESPACE, "\n", 5),
|
||||
);
|
||||
|
||||
$parser = new ConfigStaticManifestTest_Parser($tokens);
|
||||
$parser->parse();
|
||||
|
||||
$statics = $parser->getStatics();
|
||||
|
||||
$expected = array(
|
||||
'test' => array(
|
||||
'access' => T_PRIVATE,
|
||||
'value' => array(3)
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $statics[':ss:test2']);
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigStaticManifestTest_Parser extends SS_ConfigStaticManifest_Parser implements TestOnly {
|
||||
public function __construct($tokens) {
|
||||
$this->path = __FILE__;
|
||||
$this->tokens = $tokens;
|
||||
$this->length = count($this->tokens);
|
||||
$this->pos = 0;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user