Skip to content
Permalink
Browse files

FIX: Object::parse_class_spec failed to parse associative arrays

  • Loading branch information...
kinglozzer committed Apr 21, 2015
1 parent f2e2d4e commit a7100e9006b27e7885eb2ce851d9bc0839ca4468
Showing with 110 additions and 52 deletions.
  1. +87 −51 core/Object.php
  2. +23 −1 tests/core/ObjectTest.php
@@ -195,79 +195,115 @@ public static function parse_class_spec($classSpec) {
$tokens = token_get_all("<?php $classSpec");
$class = null;
$args = array();
$passedBracket = false;
// Keep track of the current bucket that we're putting data into
$bucket = &$args;
$bucketStack = array();
$had_ns = false;
$hadNamespace = false;
$currentKey = null;
foreach($tokens as $token) {
$tName = is_array($token) ? $token[0] : $token;
// Get the class naem
if($class == null && is_array($token) && $token[0] == T_STRING) {
// $forceResult used to allow null result to be detected
$result = $forceResult = null;
$tokenName = is_array($token) ? $token[0] : $token;
// Get the class name
if($class === null && is_array($token) && $token[0] === T_STRING) {
$class = $token[1];
} elseif(is_array($token) && $token[0] == T_NS_SEPARATOR) {
} elseif(is_array($token) && $token[0] === T_NS_SEPARATOR) {
$class .= $token[1];
$had_ns = true;
} elseif ($had_ns && is_array($token) && $token[0] == T_STRING) {
$hadNamespace = true;
} elseif($hadNamespace && is_array($token) && $token[0] === T_STRING) {
$class .= $token[1];
$had_ns = false;
$hadNamespace = false;
// Get arguments
} else if(is_array($token)) {
switch($token[0]) {
case T_CONSTANT_ENCAPSED_STRING:
$argString = $token[1];
switch($argString[0]) {
case '"':
$argString = stripcslashes(substr($argString,1,-1));
case T_CONSTANT_ENCAPSED_STRING:
$argString = $token[1];
switch($argString[0]) {
case '"':
$result = stripcslashes(substr($argString,1,-1));
break;
case "'":
$result = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1));
break;
default:
throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString");
}
break;
case "'":
$argString = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1));
case T_DNUMBER:
$result = (double)$token[1];
break;
default:
throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString");
}
$bucket[] = $argString;
break;
case T_DNUMBER:
$bucket[] = (double)$token[1];
break;
case T_LNUMBER:
$bucket[] = (int)$token[1];
break;
case T_STRING:
switch($token[1]) {
case 'true': $bucket[] = true; break;
case 'false': $bucket[] = false; break;
case 'null': $bucket[] = null; break;
default: throw new Exception("Bad T_STRING arg '{$token[1]}'");
}
break;
case T_LNUMBER:
$result = (int)$token[1];
break;
case T_ARRAY:
// Add an empty array to the bucket
$bucket[] = array();
$bucketStack[] = &$bucket;
$bucket = &$bucket[sizeof($bucket)-1];
case T_DOUBLE_ARROW:
// We've encountered an associative array (the array itself has already been
// added to the bucket), so the previous item added to the bucket is the key
end($bucket);
$currentKey = current($bucket);
array_pop($bucket);
break;
case T_STRING:
switch($token[1]) {
case 'true': $result = true; break;
case 'false': $result = false; break;
case 'null': $result = null; $forceResult = true; break;
default: throw new Exception("Bad T_STRING arg '{$token[1]}'");
}
break;
case T_ARRAY:
$result = array();
break;
}
} else {
if($tName == '[') {
// Add an empty array to the bucket
$bucket[] = array();
$bucketStack[] = &$bucket;
$bucket = &$bucket[sizeof($bucket)-1];
} elseif($tName == ')' || $tName == ']') {
// Pop-by-reference
$bucket = &$bucketStack[sizeof($bucketStack)-1];
if($tokenName === '[') {
$result = array();
} elseif(($tokenName === ')' || $tokenName === ']') && ! empty($bucketStack)) {
// Store the bucket we're currently working on
$oldBucket = $bucket;
// Fetch the key for the bucket at the top of the stack
end($bucketStack);
$key = key($bucketStack);
reset($bucketStack);
// Re-instate the bucket from the top of the stack
$bucket = &$bucketStack[$key];
// Add our saved, "nested" bucket to the bucket we just popped off the stack
$bucket[$key] = $oldBucket;
// Remove the bucket we just popped off the stack
array_pop($bucketStack);
}
}
// If we've got something to add to the bucket, add it
if($result !== null || $forceResult) {
if($currentKey) {
$bucket[$currentKey] = $result;
$currentKey = null;
} else {
$bucket[] = $result;
}
// If we've just pushed an array, that becomes our new bucket
if($result === array()) {
// Fetch the key that the array was pushed to
end($bucket);
$key = key($bucket);
reset($bucket);
// Store reference to "old" bucket in the stack
$bucketStack[$key] = &$bucket;
// Set the active bucket to be our newly-pushed, empty array
$bucket = &$bucket[$key];
}
}
}
return array($class, $args);
@@ -417,7 +417,7 @@ public function testParseClassSpec() {
// 5.4 Shorthand Array
$this->assertEquals(
array('Enum',array(array('Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted')),
Object::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', 'Unsubmitted'), 'Unsubmitted']")
Object::parse_class_spec("Enum(['Accepted', 'Pending', 'Declined', 'Unsubmitted'], 'Unsubmitted')")
);
// 5.4 Nested shorthand array
$this->assertEquals(
@@ -426,6 +426,28 @@ public function testParseClassSpec() {
Object::parse_class_spec(
"Enum(['Accepted', 'Pending', 'Declined', ['UnsubmittedA','UnsubmittedB']], 'Unsubmitted')")
);
// Associative array
$this->assertEquals(
array('Varchar', array(255, array('nullifyEmpty' => false))),
Object::parse_class_spec("Varchar(255, array('nullifyEmpty' => false))")
);
// Nested associative array
$this->assertEquals(
array('Test', array('string', array('nested' => array('foo' => 'bar')))),
Object::parse_class_spec("Test('string', array('nested' => array('foo' => 'bar')))")
);
// 5.4 shorthand associative array
$this->assertEquals(
array('Varchar', array(255, array('nullifyEmpty' => false))),
Object::parse_class_spec("Varchar(255, ['nullifyEmpty' => false])")
);
// 5.4 shorthand nested associative array
$this->assertEquals(
array('Test', array('string', array('nested' => array('foo' => 'bar')))),
Object::parse_class_spec("Test('string', ['nested' => ['foo' => 'bar']])")
);
// Namespaced class
$this->assertEquals(
array('Test\MyClass', array()),

0 comments on commit a7100e9

Please sign in to comment.
You can’t perform that action at this time.