Skip to content

Commit

Permalink
[jan] Add support for chomp operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Schneider committed Dec 7, 2018
1 parent be8e031 commit 382874c
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 152 deletions.
2 changes: 1 addition & 1 deletion lib/Horde/Yaml.php
Expand Up @@ -74,7 +74,7 @@ public static function load($yaml)
if (strpos($yaml, "\r") !== false) {
$yaml = str_replace(array("\r\n", "\r"), array("\n", "\n"), $yaml);
}
$lines = explode("\n", $yaml);
$lines = explode("\n", rtrim($yaml, "\n"));
$loader = new Horde_Yaml_Loader;

foreach ($lines as $line) {
Expand Down
4 changes: 3 additions & 1 deletion lib/Horde/Yaml/Dumper.php
Expand Up @@ -234,7 +234,9 @@ protected function _fold($value, $indent)
$indent += $this->_options['indent'];
$indent = str_repeat(' ', $indent);
$wrapped = wordwrap($value, $this->_options['wordwrap'], "\n$indent");
$value = ">\n" . $indent . $wrapped;
$value = '>'
. (preg_match('/\n$/', $value) ? '' : '-')
. "\n" . $indent . $wrapped;
}

return $value;
Expand Down
304 changes: 166 additions & 138 deletions lib/Horde/Yaml/Loader.php
Expand Up @@ -76,14 +76,15 @@ class Horde_Yaml_Loader
protected $_inBlock = false;

/**
* The chomp mode (strip: '-', keep: '+', clip: '')
* @var string
*/
protected $_lineEnd = '';
protected $_chomp = '';

/**
* @var string
*/
protected $_blockEnd = '';
protected $_lineEnd = '';

/**
* @var boolean
Expand Down Expand Up @@ -121,6 +122,11 @@ public function __construct()
*/
public function toArray()
{
// Chomp the final line break(s) if necessary;
if ($this->_inBlock) {
$this->_chomp();
}

// Here we travel through node-space and pick out references
// (& and *).
$this->_linkReferences();
Expand Down Expand Up @@ -157,152 +163,159 @@ public function parse($line)
}
if ($this->_inBlock && empty($trimmed)) {
$last =& $this->_allNodes[$this->_lastNode];
$last->data[key($last->data)] .=
($this->_inBlock == '>' && $line !== false ? "\n" : $this->_blockEnd);
} elseif ($this->_inBlock ||
(!$this->_inBlock &&
$trimmed[0] != '#' &&
substr($trimmed, 0, 3) != '---')) {
// Create a new node and get its indent
$node = new Horde_Yaml_Node($this->_nodeId++);
$node->indent = $this->_getIndent($line);

// Check where the node lies in the hierarchy
if ($this->_lastIndent == $node->indent) {
// If we're in a block, add the text to the parent's data
if ($this->_inBlock) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= $this->_lineEnd
. preg_replace(
'/^ {' . $this->_lastIndent . '}/',
'',
$line
);
} else {
// The current node's parent is the same as the previous
// node's
if (isset($this->_allNodes[$this->_lastNode])) {
$node->parent = $this->_allNodes[$this->_lastNode]->parent;
}
$key = key($last->data);
if ($this->_chomp != '+') {
$last->data[$key] = rtrim($last->data[$key]);
}
if ($this->_chomp != '-') {
$last->data[$key] .= "\n";
}
return;
}
if (!$this->_inBlock &&
(substr($trimmed, 0, 1) == '#' ||
substr($trimmed, 0, 3) == '---')) {
return;
}

// Create a new node and get its indent
$node = new Horde_Yaml_Node($this->_nodeId++);
$node->indent = $this->_getIndent($line);

// Check where the node lies in the hierarchy
if ($this->_lastIndent == $node->indent) {
// If we're in a block, add the text to the parent's data
if ($this->_inBlock) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= preg_replace(
'/^ {' . $this->_lastIndent . '}/',
'',
$line
) . $this->_lineEnd;
} else {
// The current node's parent is the same as the previous
// node's
if (isset($this->_allNodes[$this->_lastNode])) {
$node->parent = $this->_allNodes[$this->_lastNode]->parent;
}
} elseif ($this->_lastIndent < $node->indent) {
if ($this->_inBlock) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= $this->_lineEnd
. preg_replace(
'/^ {' . $this->_lastIndent . '}/',
'',
$line
);
} else {
// The current node's parent is the previous node
$node->parent = $this->_lastNode;

// If the value of the last node's data was > or | we need
// to start blocking i.e. taking in all lines as a text
// value until we drop our indent.
$parent =& $this->_allNodes[$node->parent];
$parent->children = true;
if (is_array($parent->data)) {
$key = key($parent->data);
if (isset($parent->data[$key])) {
$chk = $parent->data[$key];
if (!is_array($chk) &&
preg_match('/^(>|\|)([-+\d]*)/', $chk, $match)) {
if ($match[1] == '>') {
$this->_lineEnd = ' ';
} else {
$this->_lineEnd = "\n";
$this->_blockEnd = "\n";
}
if ($match[2]) {
if (strpos($match[2], '-') !== false) {
$this->_blockEnd = '';
}
$match[2] = str_replace(
array('-', '+'), '', $match[2]
);
}
if ($match[2]) {
$this->_lastIndent = $match[2];
} else {
$this->_lastIndent = $node->indent;
}
} elseif ($this->_lastIndent < $node->indent) {
if ($this->_inBlock) {
$parent =& $this->_allNodes[$this->_lastNode];
$parent->data[key($parent->data)] .= preg_replace(
'/^ {' . $this->_lastIndent . '}/',
'',
$line
) . $this->_lineEnd;
} else {
// The current node's parent is the previous node
$node->parent = $this->_lastNode;

// If the value of the last node's data was > or | we need
// to start blocking i.e. taking in all lines as a text
// value until we drop our indent.
$parent =& $this->_allNodes[$node->parent];
$parent->children = true;
if (is_array($parent->data)) {
$key = key($parent->data);
if (isset($parent->data[$key])) {
$chk = $parent->data[$key];
if (!is_array($chk) &&
preg_match('/^(>|\|)([-+\d]*)/', $chk, $match)) {
if ($match[1] == '>') {
$this->_lineEnd = ' ';
} else {
$this->_lineEnd = "\n";
}
$this->_chomp = '';
if ($match[2]) {
if (strpos($match[2], '-') !== false) {
$this->_chomp = '-';
} elseif (strpos($match[2], '+') !== false) {
$this->_chomp = '+';
}
$this->_inBlock = $match[1];
$parent->data[$key] = str_replace(
$match[0], '', $parent->data[$key]
);
$parent->data[$key] .= preg_replace(
'/^ {' . $this->_lastIndent . '}/',
'',
$line
$match[2] = str_replace(
array('-', '+'), '', $match[2]
);
$parent->children = false;
}
if ($match[2]) {
$this->_lastIndent = $match[2];
} else {
$this->_lastIndent = $node->indent;
}
$this->_inBlock = $match[1];
$parent->data[$key] = str_replace(
$match[0], '', $parent->data[$key]
);
$parent->data[$key] .= preg_replace(
'/^ {' . $this->_lastIndent . '}/',
'',
$line
) . $this->_lineEnd;
$parent->children = false;
}
}
}
} elseif ($this->_lastIndent > $node->indent) {
// Any block we had going is dead now
if ($this->_inBlock) {
$this->_inBlock = false;
$last =& $this->_allNodes[$this->_lastNode];
$last->data[key($last->data)] .= $this->_blockEnd;
$this->_blockEnd = '';
}
}
} elseif ($this->_lastIndent > $node->indent) {
// Any block we had going is dead now
if ($this->_inBlock) {
$this->_inBlock = false;
$this->_chomp();
}

// We don't know the parent of the node so we have to
// find it
foreach ($this->_indentSort[$node->indent] as $n) {
if ($n->indent == $node->indent) {
$node->parent = $n->parent;
}
// We don't know the parent of the node so we have to
// find it
foreach ($this->_indentSort[$node->indent] as $n) {
if ($n->indent == $node->indent) {
$node->parent = $n->parent;
break;
}
}
}

if (!$this->_inBlock) {
// Set these properties with information from our
// current node
$this->_lastIndent = $node->indent;

// Set the last node
$this->_lastNode = $node->id;

// Parse the YAML line and return its data
$node->data = $this->_parseLine($line);

// Add the node to the master list
$this->_allNodes[$node->id] = $node;

// Add a reference to the parent list
$this->_allParent[intval($node->parent)][] = $node->id;

// Add a reference to the node in an indent array
$this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id];

// Add a reference to the node in a References array
// if this node has a YAML reference in it.
$is_array = is_array($node->data);
$key = key($node->data);
$isset = isset($node->data[$key]);
if ($isset) {
$nodeval = $node->data[$key];
if ($is_array) {
if (!is_array($nodeval) && !is_object($nodeval) &&
strlen($nodeval) &&
($nodeval[0] == '&' || $nodeval[0] == '*') &&
isset($nodeval[1]) && $nodeval[1] != ' ') {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
} elseif (is_array($nodeval)) {
// Incomplete reference making code. Needs to be
// cleaned up.
foreach ($node->data[$key] as $d) {
if (!is_array($d) &&
strlen($d) &&
($d[0] == '&' || $d[0] == '*') &&
isset ($d[1]) && $d[1] != ' ') {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
}
if (!$this->_inBlock) {
// Set these properties with information from our
// current node
$this->_lastIndent = $node->indent;

// Set the last node
$this->_lastNode = $node->id;

// Parse the YAML line and return its data
$node->data = $this->_parseLine($line);

// Add the node to the master list
$this->_allNodes[$node->id] = $node;

// Add a reference to the parent list
$this->_allParent[intval($node->parent)][] = $node->id;

// Add a reference to the node in an indent array
$this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id];

// Add a reference to the node in a References array
// if this node has a YAML reference in it.
$is_array = is_array($node->data);
$key = key($node->data);
$isset = isset($node->data[$key]);
if ($isset) {
$nodeval = $node->data[$key];
if ($is_array) {
if (!is_array($nodeval) && !is_object($nodeval) &&
strlen($nodeval) &&
($nodeval[0] == '&' || $nodeval[0] == '*') &&
isset($nodeval[1]) && $nodeval[1] != ' ') {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
} elseif (is_array($nodeval)) {
// Incomplete reference making code. Needs to be
// cleaned up.
foreach ($node->data[$key] as $d) {
if (!is_array($d) &&
strlen($d) &&
($d[0] == '&' || $d[0] == '*') &&
isset ($d[1]) && $d[1] != ' ') {
$this->_haveRefs[] =& $this->_allNodes[$node->id];
}
}
}
Expand All @@ -311,6 +324,21 @@ public function parse($line)
}
}

/**
* Chomps trailing white space if necessary.
*/
protected function _chomp()
{
$last =& $this->_allNodes[$this->_lastNode];
$key = key($last->data);
if (!$this->_chomp) {
$last->data[$key] = rtrim($last->data[$key]) . "\n";
}
if ($this->_chomp == '-') {
$last->data[$key] = rtrim($last->data[$key]);
}
}

/**
* Finds and returns the indentation of a YAML line.
*
Expand Down Expand Up @@ -503,7 +531,7 @@ protected function _unserialize(&$data)
throw new Horde_Yaml_Exception("$class does not implement Serializable");
}

$class_data = substr($data, $first_space + 1);
$class_data = trim(substr($data, $first_space + 1));
$serialized = 'C:' . strlen($class) . ':"' . $class . '":' . strlen($class_data) . ':{' . $class_data . '}';
$data = unserialize($serialized);
break;
Expand Down
4 changes: 2 additions & 2 deletions test/Horde/Yaml/DumperTest.php
Expand Up @@ -167,7 +167,7 @@ public function testSerializable()
{
$value = array('obj' => new Horde_Yaml_Test_Serializable('s'));

$expected = "---\nobj: >\n !php/object::Horde_Yaml_Test_Serializable\n s\n";
$expected = "---\nobj: >-\n !php/object::Horde_Yaml_Test_Serializable\n s\n";
$actual = $this->dumper->dump($value);
$this->assertEquals($expected, $actual);
}
Expand Down Expand Up @@ -221,7 +221,7 @@ public function testShouldNotWrapStringsWithCommentDelimiterForFoldedStrings()
// stringWithHash: 'string # this is part of the string, not a comment'
$value = array('foo' => 'string # this is not a comment but it is a long string that gets folded', 'bar' => 2);
$expected = "---\n"
. "foo: >\n"
. "foo: >-\n"
. " string # this is not a comment but it is\n"
. " a long string that gets folded\n"
. "bar: 2\n";
Expand Down

0 comments on commit 382874c

Please sign in to comment.