diff --git a/admin/javascript/LeftAndMain.ActionTabSet.js b/admin/javascript/LeftAndMain.ActionTabSet.js index dbbf3da22..f7c54db43 100644 --- a/admin/javascript/LeftAndMain.ActionTabSet.js +++ b/admin/javascript/LeftAndMain.ActionTabSet.js @@ -30,7 +30,11 @@ // because the panel is still open when the ajax edit form reloads. var frame = $('.cms-container').find('iframe'); frame.each(function(index, iframe){ - $(iframe).contents().off('click.ss-ui-action-tabset'); + try { + $(iframe).contents().off('click.ss-ui-action-tabset'); + } catch (e) { + console.warn('Unable to access iframe, possible https mis-match'); + } }); $(document).off('click.ss-ui-action-tabset'); diff --git a/core/Object.php b/core/Object.php index e9a51a96e..eb9ec9310 100755 --- a/core/Object.php +++ b/core/Object.php @@ -220,19 +220,23 @@ abstract class Object { // 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]) { @@ -240,52 +244,85 @@ abstract class Object { $argString = $token[1]; switch($argString[0]) { case '"': - $argString = stripcslashes(substr($argString,1,-1)); + $result = stripcslashes(substr($argString,1,-1)); break; case "'": - $argString = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1)); + $result = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1)); break; default: throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString"); } - $bucket[] = $argString; + break; case T_DNUMBER: - $bucket[] = (double)$token[1]; + $result = (double)$token[1]; break; case T_LNUMBER: - $bucket[] = (int)$token[1]; + $result = (int)$token[1]; + break; + + 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': $bucket[] = true; break; - case 'false': $bucket[] = false; break; - case 'null': $bucket[] = null; break; + 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: - // Add an empty array to the bucket - $bucket[] = array(); - $bucketStack[] = &$bucket; - $bucket = &$bucket[sizeof($bucket)-1]; + $result = array(); + break; + } + } else { + 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; } - } 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]; - array_pop($bucketStack); + // 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]; } } } diff --git a/tests/core/ObjectTest.php b/tests/core/ObjectTest.php index 487affb97..1e766a5fb 100644 --- a/tests/core/ObjectTest.php +++ b/tests/core/ObjectTest.php @@ -440,7 +440,7 @@ class ObjectTest extends SapphireTest { // 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( @@ -449,6 +449,28 @@ class ObjectTest extends SapphireTest { 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()),