Commit 29f3ea0d authored by yosymfony's avatar yosymfony
Browse files

Fixed the bug #24: "Wrong array of tables implementation"

parent 7c60cdf8
......@@ -4,6 +4,8 @@ CHANGELOG
-----
* `TomlBuilder` does not throw a `DumpException` anymore when the character "#" appears in a quoted key.
* The method `addArrayTables` from the class `TomlBuilder` has been declared as deprecated. Use the method `addArrayOfTable` instead.
* Fixed the bug #24: "Wrong array of tables implementation".
* A new class `TomlArray` has been added to deal with the Toml array generation.
1.0.2 (2018-06-29)
------------------
......
......@@ -25,8 +25,8 @@ class Parser extends AbstractParser
{
/** @var KeyStore */
private $keyStore;
private $result = [];
private $workArray;
/** @var TomlArray */
private $tomlArray;
private static $tokensNotAllowedInBasicStrings = [
'T_ESCAPE',
......@@ -60,13 +60,13 @@ class Parser extends AbstractParser
protected function parseImplementation(TokenStream $ts) : array
{
$this->keyStore = new KeyStore();
$this->resetWorkArrayToResultArray();
$this->tomlArray = new TomlArray();
while ($ts->hasPendingTokens()) {
$this->processExpression($ts);
}
return $this->result;
return $this->tomlArray->getArray();
}
private function processExpression(TokenStream $ts) : void
......@@ -116,15 +116,15 @@ class Parser extends AbstractParser
$this->syntaxError("The key \"{$keyName}\" has already been defined previously.");
}
$this->keyStore->addkey($keyName);
$this->keyStore->addKey($keyName);
}
if ($ts->isNext('T_LEFT_SQUARE_BRAKET')) {
$this->workArray[$keyName] = $this->parseArray($ts);
$this->tomlArray->addKeyValue($keyName, $this->parseArray($ts));
} elseif ($isInlineTable) {
$this->parseInlineTable($ts, $keyName);
} else {
$this->workArray[$keyName] = $this->parseSimpleValue($ts)->value;
$this->tomlArray->addKeyValue($keyName, $this->parseSimpleValue($ts)->value);
}
if (!$isFromInlineTable) {
......@@ -441,9 +441,8 @@ class Parser extends AbstractParser
private function parseInlineTable(TokenStream $ts, string $keyName) : void
{
$this->matchNext('T_LEFT_CURLY_BRACE', $ts);
$priorWorkArray = &$this->workArray;
$this->addArrayKeyToWorkArray($keyName);
$this->tomlArray->beginInlineTableKey($keyName);
$this->parseSpaceIfExists($ts);
......@@ -461,25 +460,21 @@ class Parser extends AbstractParser
}
$this->matchNext('T_RIGHT_CURLY_BRACE', $ts);
$this->workArray = &$priorWorkArray;
$this->tomlArray->endCurrentInlineTableKey();
}
private function parseTable(TokenStream $ts) : void
{
$this->matchNext('T_LEFT_SQUARE_BRAKET', $ts);
$fullTableName = $key = $this->parseKeyName($ts);
$this->resetWorkArrayToResultArray();
$this->addArrayKeyToWorkArray($key);
$fullTableName = $this->tomlArray->escapeKey($key = $this->parseKeyName($ts));
while ($ts->isNext('T_DOT')) {
$ts->moveNext();
$key = $this->parseKeyName($ts);
$key = $this->tomlArray->escapeKey($this->parseKeyName($ts));
$fullTableName .= ".$key";
$this->addArrayKeyToWorkArray($key);
}
if (!$this->keyStore->isValidTableKey($fullTableName)) {
......@@ -487,6 +482,7 @@ class Parser extends AbstractParser
}
$this->keyStore->addTableKey($fullTableName);
$this->tomlArray->addTableKey($fullTableName);
$this->matchNext('T_RIGHT_SQUARE_BRAKET', $ts);
$this->parseSpaceIfExists($ts);
......@@ -499,18 +495,13 @@ class Parser extends AbstractParser
$this->matchNext('T_LEFT_SQUARE_BRAKET', $ts);
$this->matchNext('T_LEFT_SQUARE_BRAKET', $ts);
$fullTableName = $key = $this->parseKeyName($ts);
$this->resetWorkArrayToResultArray();
$this->addArrayOfTableKeyToWorkArray($key, !$ts->isNext('T_DOT'));
$fullTableName = $key = $this->tomlArray->escapeKey($this->parseKeyName($ts));
while ($ts->isNext('T_DOT')) {
$ts->moveNext();
$key = $this->parseKeyName($ts);
$key = $this->tomlArray->escapeKey($this->parseKeyName($ts));
$fullTableName .= ".$key";
$this->addArrayOfTableKeyToWorkArray($key, !$ts->isNext('T_DOT'));
}
if (!$this->keyStore->isValidArrayTableKey($fullTableName)) {
......@@ -522,6 +513,7 @@ class Parser extends AbstractParser
}
$this->keyStore->addArrayTableKey($fullTableName);
$this->tomlArray->addArrayTableKey($fullTableName);
$this->matchNext('T_RIGHT_SQUARE_BRAKET', $ts);
$this->matchNext('T_RIGHT_SQUARE_BRAKET', $ts);
......@@ -565,45 +557,6 @@ class Parser extends AbstractParser
}
}
private function addArrayKeyToWorkArray(string $keyName) : void
{
if ($this->keyStore->isRegisteredAsArrayTableKey($keyName)) {
$this->addArrayOfTableKeyToWorkArray($keyName, false);
return;
}
if (isset($this->workArray[$keyName]) === false) {
$this->workArray[$keyName] = [];
}
$this->workArray = &$this->workArray[$keyName];
}
private function addArrayOfTableKeyToWorkArray(string $keyName, bool $islast) : void
{
if (isset($this->workArray[$keyName]) === false) {
$this->workArray[$keyName] = [];
$this->workArray[$keyName][] = [];
} elseif ($islast) {
$this->workArray[$keyName][] = [];
}
if (!$this->keyStore->isRegisteredAsTableKey($keyName)) {
end($this->workArray[$keyName]);
$this->workArray = &$this->workArray[$keyName][key($this->workArray[$keyName])];
return;
}
$this->workArray = &$this->workArray[$keyName];
}
private function resetWorkArrayToResultArray() : void
{
$this->workArray = &$this->result;
}
private function errorIfNextIsNotNewlineOrEOS(TokenStream $ts) : void
{
if (!$ts->isNextAny(['T_NEWLINE', 'T_EOS'])) {
......
<?php
/*
* This file is part of the Yosymfony\Toml package.
*
* (c) YoSymfony <http://github.com/yosymfony>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Yosymfony\Toml;
/**
* Internal class for managing a Toml array
*
* @author Victor Puertas <vpgugr@vpgugr.com>
*/
class TomlArray
{
private const DOT_ESCAPED = '%*%';
private $result = [];
private $currentPointer;
private $originInlineTableCurrentPointer;
private $ArrayTableKeys = [];
private $inlineTablePointers = [];
public function __construct()
{
$this->resetCurrentPointer();
}
public function addKeyValue(string $name, $value) : Void
{
$this->currentPointer[$name] = $value;
}
public function addTableKey(string $name) : Void
{
$this->resetCurrentPointer();
$this->goToKey($name);
}
public function beginInlineTableKey(string $name) : Void
{
$this->inlineTablePointers[] = &$this->currentPointer;
$this->goToKey($name);
}
public function endCurrentInlineTableKey() : Void
{
$indexLastElement = $this->getKeyLastElementOfArray($this->inlineTablePointers);
$this->currentPointer = &$this->inlineTablePointers[$indexLastElement];
unset($this->inlineTablePointers[$indexLastElement]);
}
public function addArrayTableKey(string $name) : Void
{
$this->resetCurrentPointer();
$this->goToKey($name);
$this->currentPointer[] = [];
$this->setCurrentPointerToLastElement();
if (!$this->existsInArrayTableKey($name)) {
$this->ArrayTableKeys[] = $name;
}
}
public function escapeKey(string $name) : string
{
return \str_replace('.', self::DOT_ESCAPED, $name);
}
public function getArray() : array
{
return $this->result;
}
private function unescapeKey(string $name) : string
{
return \str_replace(self::DOT_ESCAPED, '.', $name);
}
private function goToKey(string $name) : Void
{
$keyParts = explode('.', $name);
$accumulatedKey = '';
$countParts = count($keyParts);
foreach ($keyParts as $index => $keyPart) {
$keyPart = $this->unescapeKey($keyPart);
$isLastKeyPart = $index == $countParts -1;
$accumulatedKey .= $accumulatedKey == '' ? $keyPart : '.'.$keyPart;
if (\array_key_exists($keyPart, $this->currentPointer) === false) {
$this->currentPointer[$keyPart] = [];
}
$this->currentPointer = &$this->currentPointer[$keyPart];
if ($this->existsInArrayTableKey($accumulatedKey) && !$isLastKeyPart) {
$this->setCurrentPointerToLastElement();
continue;
}
}
}
private function setCurrentPointerToLastElement() : void
{
$indexLastElement = $this->getKeyLastElementOfArray($this->currentPointer);
$this->currentPointer = &$this->currentPointer[$indexLastElement];
}
private function resetCurrentPointer() : Void
{
$this->currentPointer = &$this->result;
}
private function existsInArrayTableKey($name) : bool
{
return \in_array($this->unescapeKey($name), $this->ArrayTableKeys);
}
private function getKeyLastElementOfArray(array &$arr)
{
end($arr);
return key($arr);
}
}
......@@ -689,11 +689,9 @@ toml;
$this->assertEquals([
'albums' => [
[
'songs' => [
[
'name' => 'Glory Days'
],
'songs' => [
[
'name' => 'Glory Days'
],
],
],
......
<?php
/*
* This file is part of the Yosymfony Toml.
*
* (c) YoSymfony <http://github.com/yosymfony>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Yosymfony\Toml\tests;
use PHPUnit\Framework\TestCase;
use Yosymfony\Toml\TomlArray;
class TomlArrayTest extends TestCase
{
/** @var TomlArray */
private $tomlArray;
public function setUp()
{
$this->tomlArray = new TomlArray();
}
public function tearDown()
{
$this->tomlArray = null;
}
public function testGetArrayMustReturnTheKeyValueAdded() : void
{
$this->tomlArray->addKeyValue('company', 'acme');
$this->assertEquals([
'company' => 'acme'
], $this->tomlArray->getArray());
}
public function testGetArrayMustReturnTheTableWithTheKeyValue() : void
{
$this->tomlArray->addTableKey('companyData');
$this->tomlArray->addKeyValue('company', 'acme');
$this->assertEquals([
'companyData' => [
'company' => 'acme'
],
], $this->tomlArray->getArray());
}
public function testGetArrayMustReturnAnArrayOfTables() : void
{
$this->tomlArray->addArrayTableKey('companyData');
$this->tomlArray->addKeyValue('company', 'acme1');
$this->tomlArray->addArrayTableKey('companyData');
$this->tomlArray->addKeyValue('company', 'acme2');
$this->assertEquals([
'companyData' => [
[
'company' => 'acme1'
],
[
'company' => 'acme2'
],
]
], $this->tomlArray->getArray());
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment