Skip to content

Commit a2b967e

Browse files
committed
v1.0.2
1 parent 3ec8721 commit a2b967e

File tree

2 files changed

+119
-31
lines changed

2 files changed

+119
-31
lines changed

Changelog.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ Versioning guidelines for SemVer can be found at: https://semver.org/
99

1010
(none)
1111

12+
=== Version/Release 1.0.2 ===
13+
MAINTENANCE RELEASE.
14+
15+
- [2021.05.22; Maikuolan]: Synced the YAML class to its latest version.
16+
17+
Caleb M (Maikuolan),
18+
May 22, 2021.
19+
1220
=== Version/Release 1.0.1 ===
1321
MAINTENANCE RELEASE.
1422

YAML.php

Lines changed: 111 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* YAML handler (last modified: 2019.12.26).
3+
* YAML handler (last modified: 2021.05.22).
44
*
55
* This file is a part of the "common classes package", utilised by a number of
66
* packages and projects, including CIDRAM and phpMussel.
@@ -9,34 +9,63 @@
99
* License: GNU/GPLv2
1010
* @see LICENSE.txt
1111
*
12-
* "COMMON CLASSES PACKAGE" COPYRIGHT 2019 and beyond by Caleb Mazalevskis
13-
* (Maikuolan). Earliest iteration and deployment of "YAML handler" COPYRIGHT
14-
* 2016 and beyond by Caleb Mazalevskis (Maikuolan).
12+
* "COMMON CLASSES PACKAGE" COPYRIGHT 2019 and beyond by Caleb Mazalevskis.
13+
* *This particular class*, COPYRIGHT 2016 and beyond by Caleb Mazalevskis.
1514
*
16-
* Note: The YAML handler is intended to adequately serve the needs of the
17-
* packages and projects where it is implemented, but isn't a complete YAML
18-
* solution, instead supporting the YAML specification only to the bare minimum
19-
* required by those packages and projects known to implement it.
15+
* Note: Some parts of the YAML specification aren't supported by this class.
16+
* See the included documentation for more information.
2017
*/
2118

2219
namespace Maikuolan\Common;
2320

2421
class YAML
2522
{
26-
/** An array to contain all the data processed by the handler. */
23+
/**
24+
* @var array An array to contain all the data processed by the handler.
25+
*/
2726
public $Data = [];
2827

29-
/** Flag used for rendering multi-line values. */
28+
/**
29+
* @var bool Whether to render multi-line values.
30+
*/
3031
private $MultiLine = false;
3132

33+
/**
34+
* @var bool Whether to render folded multi-line values.
35+
*/
36+
private $MultiLineFolded = false;
37+
38+
/**
39+
* @var string Default indent to use when reconstructing YAML data.
40+
*/
41+
public $Indent = ' ';
42+
43+
/**
44+
* @var int Single line to folded multi-line string length limit.
45+
*/
46+
public $FoldedAt = 120;
47+
48+
/**
49+
* @var array Used to cache any anchors found in the document.
50+
*/
51+
public $Anchors = [];
52+
53+
/**
54+
* @var string The tag/release the version of this file belongs to (might
55+
* be needed by some implementations to ensure compatibility).
56+
* @link https://github.com/Maikuolan/Common/tags
57+
*/
58+
const VERSION = '1.6.1';
59+
3260
/**
3361
* Can optionally begin processing data as soon as the object is
3462
* instantiated, or just instantiate first, and manually make any needed
3563
* calls afterwards (though the former is recommended over the latter).
3664
*
3765
* @param string $In The data to process.
66+
* @return void
3867
*/
39-
public function __construct(string $In = '')
68+
public function __construct($In = '')
4069
{
4170
if ($In) {
4271
$this->process($In, $this->Data);
@@ -49,9 +78,30 @@ public function __construct(string $In = '')
4978
* @param string|int|bool $Value The value to be normalised.
5079
* @param int $ValueLen The length of the value to be normalised.
5180
* @param string|int|bool $ValueLow The value to be normalised, lowercased.
81+
* @return void
5282
*/
53-
private function normaliseValue(&$Value, int $ValueLen, $ValueLow)
83+
private function normaliseValue(&$Value, $ValueLen, $ValueLow)
5484
{
85+
/** Check for anchors and populate if necessary. */
86+
$AnchorMatches = [];
87+
if (
88+
preg_match('~^&([\dA-Za-z]+) +(.*)$~', $Value, $AnchorMatches) &&
89+
isset($AnchorMatches[1], $AnchorMatches[2])
90+
) {
91+
$Value = $AnchorMatches[2];
92+
$this->Anchors[$AnchorMatches[1]] = $Value;
93+
$ValueLen = strlen($Value);
94+
$ValueLow = strtolower($Value);
95+
} elseif (
96+
preg_match('~^\*([\dA-Za-z]+)$~', $Value, $AnchorMatches) &&
97+
isset($AnchorMatches[1], $this->Anchors[$AnchorMatches[1]])
98+
) {
99+
$Value = $this->Anchors[$AnchorMatches[1]];
100+
$ValueLen = strlen($Value);
101+
$ValueLow = strtolower($Value);
102+
}
103+
104+
/** Check for string quotes. */
55105
foreach ([
56106
['"', '"', 1],
57107
["'", "'", 1],
@@ -66,15 +116,18 @@ private function normaliseValue(&$Value, int $ValueLen, $ValueLow)
66116
return;
67117
}
68118
}
69-
if ($ValueLow === 'true' || $ValueLow === 'y') {
119+
120+
if ($ValueLow === 'true' || $ValueLow === 'y' || $Value === '+') {
70121
$Value = true;
71-
} elseif ($ValueLow === 'false' || $ValueLow === 'n') {
122+
} elseif ($ValueLow === 'false' || $ValueLow === 'n' || $Value === '-') {
72123
$Value = false;
124+
} elseif ($ValueLow === 'null' || $Value === '~') {
125+
$Value = null;
73126
} elseif (substr($Value, 0, 2) === '0x' && ($HexTest = substr($Value, 2)) && !preg_match('/[^\da-f]/i', $HexTest) && !($ValueLen % 2)) {
74127
$Value = hex2bin($HexTest);
75128
} elseif (preg_match('~^\d+$~', $Value)) {
76129
$Value = (int)$Value;
77-
} elseif (preg_match('~^\d+\.\d+$~', $Value)) {
130+
} elseif (preg_match('~^(?:\d+\.\d+|\d+(?:\.\d+)?[Ee][-+]\d+)$~', $Value)) {
78131
$Value = (float)$Value;
79132
} elseif (!$ValueLen) {
80133
$Value = false;
@@ -89,13 +142,14 @@ private function normaliseValue(&$Value, int $ValueLen, $ValueLow)
89142
* @param int $Depth Tab depth (inherited through recursion; ignore it).
90143
* @return bool True when entire process completes successfully. False to exit early.
91144
*/
92-
public function process(string $In, array &$Arr, int $Depth = 0): bool
145+
public function process($In, array &$Arr, $Depth = 0)
93146
{
94-
if (strpos($In, "\n") === false) {
147+
if (!is_string($In) || strpos($In, "\n") === false) {
95148
return false;
96149
}
97150
if ($Depth === 0) {
98151
$this->MultiLine = false;
152+
$this->MultiLineFolded = false;
99153
}
100154
$In = str_replace("\r", '', $In);
101155
$Key = $Value = $SendTo = '';
@@ -117,11 +171,15 @@ public function process(string $In, array &$Arr, int $Depth = 0): bool
117171
if ($TabLen === 0) {
118172
$TabLen = $ThisTab;
119173
}
120-
if (!$this->MultiLine) {
174+
if (!$this->MultiLine && !$this->MultiLineFolded) {
121175
$SendTo .= $ThisLine . "\n";
122176
} else {
123177
if ($SendTo) {
124-
$SendTo .= "\n";
178+
if ($this->MultiLine) {
179+
$SendTo .= "\n";
180+
} elseif (substr($ThisLine, $TabLen, 1) !== ' ' && substr($SendTo, -1) !== ' ') {
181+
$SendTo .= ' ';
182+
}
125183
}
126184
$SendTo .= substr($ThisLine, $TabLen);
127185
}
@@ -132,7 +190,7 @@ public function process(string $In, array &$Arr, int $Depth = 0): bool
132190
if (empty($Key)) {
133191
return false;
134192
}
135-
if (!$this->MultiLine) {
193+
if (!$this->MultiLine && !$this->MultiLineFolded) {
136194
if (!isset($Arr[$Key]) || !is_array($Arr[$Key])) {
137195
$Arr[$Key] = [];
138196
}
@@ -149,7 +207,7 @@ public function process(string $In, array &$Arr, int $Depth = 0): bool
149207
}
150208
}
151209
if ($SendTo && !empty($Key)) {
152-
if (!$this->MultiLine) {
210+
if (!$this->MultiLine && !$this->MultiLineFolded) {
153211
if (!isset($Arr[$Key]) || !is_array($Arr[$Key])) {
154212
$Arr[$Key] = [];
155213
}
@@ -173,9 +231,13 @@ public function process(string $In, array &$Arr, int $Depth = 0): bool
173231
* @param array $Arr Where to store the data.
174232
* @return bool True when entire process completes successfully. False to exit early.
175233
*/
176-
private function processLine(string &$ThisLine, int &$ThisTab, &$Key, &$Value, array &$Arr): bool
234+
private function processLine(&$ThisLine, &$ThisTab, &$Key, &$Value, array &$Arr)
177235
{
178-
if (substr($ThisLine, -1) === ':' && strpos($ThisLine, ': ') === false) {
236+
if ($ThisLine === '---') {
237+
$Key = '---';
238+
$Value = false;
239+
$Arr[$Key] = $Value;
240+
} elseif (substr($ThisLine, -1) === ':' && strpos($ThisLine, ': ') === false) {
179241
$Key = substr($ThisLine, $ThisTab, -1);
180242
$KeyLen = strlen($Key);
181243
$KeyLow = strtolower($Key);
@@ -227,6 +289,7 @@ private function processLine(string &$ThisLine, int &$ThisTab, &$Key, &$Value, a
227289
$Value = false;
228290
}
229291
$this->MultiLine = ($Value === '|');
292+
$this->MultiLineFolded = ($Value === '>');
230293
return true;
231294
}
232295

@@ -236,35 +299,42 @@ private function processLine(string &$ThisLine, int &$ThisTab, &$Key, &$Value, a
236299
* @param array $Arr The array to reconstruct from.
237300
* @param string $Out The reconstructed YAML.
238301
* @param int $Depth The level depth.
302+
* @return void
239303
*/
240-
private function processInner(array $Arr, string &$Out, int $Depth = 0)
304+
private function processInner(array $Arr, &$Out, $Depth = 0)
241305
{
242306
$Sequential = (array_keys($Arr) === range(0, count($Arr) - 1));
243307
foreach ($Arr as $Key => $Value) {
244308
if ($Key === '---' && $Value === false) {
245309
$Out .= "---\n";
246310
continue;
247311
}
248-
$ThisDepth = str_repeat(' ', $Depth);
312+
$ThisDepth = str_repeat($this->Indent, $Depth);
249313
$Out .= $ThisDepth . ($Sequential ? '-' : $Key . ':');
250314
if (is_array($Value)) {
251315
$Out .= "\n";
252316
$this->processInner($Value, $Out, $Depth + 1);
253317
continue;
254-
} else {
255-
$Out .= ' ';
256318
}
319+
$Out .= $this->Indent;
257320
if ($Value === true) {
258321
$Out .= 'true';
259322
} elseif ($Value === false) {
260323
$Out .= 'false';
324+
} elseif ($Value === null) {
325+
$Out .= 'null';
261326
} elseif (preg_match('~[^\t\n\r\x20-\x7e\xa0-\xff]~', $Value)) {
262327
$Out .= '0x' . strtolower(bin2hex($Value));
263328
} elseif (strpos($Value, "\n") !== false) {
264-
$Value = str_replace("\n", "\n" . $ThisDepth . ' ', $Value);
265-
$Out .= "|\n" . $ThisDepth . ' ' . $Value;
329+
$Value = str_replace("\n", "\n" . $ThisDepth . $this->Indent, $Value);
330+
$Out .= "|\n" . $ThisDepth . $this->Indent . $Value;
266331
} elseif (is_string($Value)) {
267-
$Out .= '"' . $Value . '"';
332+
if (strpos($Value, ' ') !== false && strlen($Value) >= $this->FoldedAt) {
333+
$Value = wordwrap($Value, $this->FoldedAt, "\n" . $ThisDepth . $this->Indent);
334+
$Out .= ">\n" . $ThisDepth . $this->Indent . $Value;
335+
} else {
336+
$Out .= '"' . $Value . '"';
337+
}
268338
} else {
269339
$Out .= $Value;
270340
}
@@ -278,10 +348,20 @@ private function processInner(array $Arr, string &$Out, int $Depth = 0)
278348
* @param array $Arr The array to reconstruct from.
279349
* @return string The reconstructed YAML.
280350
*/
281-
public function reconstruct(array $Arr): string
351+
public function reconstruct(array $Arr)
282352
{
283353
$Out = '';
284354
$this->processInner($Arr, $Out);
285355
return $Out . "\n";
286356
}
357+
358+
/**
359+
* PHP's magic "__toString" method to act as an alias for "reconstruct".
360+
*
361+
* @return string
362+
*/
363+
public function __toString()
364+
{
365+
return $this->reconstruct($this->Data);
366+
}
287367
}

0 commit comments

Comments
 (0)