Passed
Push — master ( 3aa121...7cc354 )
by stéphane
03:44
created

DumperHandlers::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Dallgoot\Yaml;
3
4
// use \SplDoublyLinkedList as DLL;
5
6
/**
7
 *  Convert PHP datatypes to a YAML string syntax
8
 *
9
 * @author  Stéphane Rebai <[email protected]>
10
 * @license Apache 2.0
11
 * @link    https://github.com/dallgoot/yaml
12
 */
13
class DumperHandlers
14
{
15
    private const INDENT = 2;
16
    private const OPTIONS = 00000;
17
    private const DATE_FORMAT = 'Y-m-d';
18
19
    private $options;
20
    private $multipleDocs = false;
21
    //options
22
    public const EXPAND_SHORT = 00001;
23
    public const SERIALIZE_CUSTOM_OBJECTS = 00010;
24
    public $floatPrecision = 4;
25
26 10
    public function __construct(int $options = null)
27
    {
28 10
        if (is_int($options)) $this->options = $options;
29 10
    }
30
31
32
33 6
    public function dump($dataType, int $indent):string
34
    {
35 6
        if(is_null($dataType)) {
36 1
            return '';
37 6
        } elseif(is_resource($dataType)) {
38 1
            return get_resource_type($dataType);
39 6
        } elseif (is_scalar($dataType)) {
40 6
            return $this->dumpScalar($dataType);
41
        } else {
42 2
            return $this->dumpCompound($dataType, $indent);
43
        }
44
    }
45
46 7
    public function dumpScalar($dataType):string
47
    {
48 7
        if ($dataType === \INF) return '.inf';
49 7
        if ($dataType === -\INF) return '-.inf';
50 7
        $precision = "%.".$this->floatPrecision."F";
51 7
        switch (gettype($dataType)) {
52 7
            case 'boolean': return $dataType ? 'true' : 'false';
53 7
            case 'float': //fall through
54 7
            case 'double': return is_nan((double) $dataType) ? '.nan' : sprintf($precision, $dataType);
55
        }
56 6
        return $this->dumpString($dataType);
57
    }
58
59
60 4
    private function dumpCompound($compound, int $indent):string
61
    {
62 4
        $iterator = null;
63 4
        $mask = '%s:';
64 4
        if (is_callable($compound)) {
65 1
            throw new \Exception("Dumping Callable|Closure is not currently supported", 1);
66 3
        } elseif ($compound instanceof YamlObject) {
67 1
            return $this->dumpYamlObject($compound);
68 3
        } elseif ($compound instanceof Compact) {
69 1
             return $this->dumpCompact($compound, $indent);
70 3
        } elseif (is_array($compound)) {
71 3
            $iterator = new \ArrayIterator($compound);
72 3
            $mask = '-';
73 3
            $refKeys = range(0, count($compound)-1);
74 3
            if (array_keys($compound) !== $refKeys) {
75 3
                $mask = '%s:';
76
            }
77 2
        } elseif (is_iterable($compound)) {
78
            $iterator = $compound;
79 2
        } elseif (is_object($compound)) {
80 2
            if ($compound instanceof Tagged)     return $this->dumpTagged($compound, $indent);
81
            //TODO:  consider dumping datetime as date strings according to a format provided by user
82 1
            if ($compound instanceof \DateTime)  return $compound->format(self::DATE_FORMAT);
83 1
            $iterator = new \ArrayIterator(get_object_vars($compound));
84
        }
85 3
        return $this->iteratorToString($iterator, $mask, $indent);
86
    }
87
88
89 2
    private function dumpYamlObject(YamlObject $obj):string
90
    {
91 2
        if ($this->multipleDocs || $obj->hasDocStart() || $obj->isTagged()) {
0 ignored issues
show
Bug introduced by
The method isTagged() does not exist on Dallgoot\Yaml\YamlObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

91
        if ($this->multipleDocs || $obj->hasDocStart() || $obj->/** @scrutinizer ignore-call */ isTagged()) {
Loading history...
92
           $this->multipleDocs = true;
93
          // && $this->$result instanceof DLL) $this->$result->push("---");
94
        }
95 2
        if (count($obj) > 0) {
96 2
            return $this->iteratorToString($obj, '-', 0);
97
        }
98 2
        return $this->iteratorToString(new \ArrayIterator(get_object_vars($obj)), '%s:', 0);
99
        // $this->insertComments($obj->getComment());
100
        //TODO: $references = $obj->getAllReferences();
101
    }
102
103
104 5
    private function iteratorToString(\Iterator $iterable, string $keyMask, int $indent):string
105
    {
106 5
        $pairs = [];
107 5
        foreach ($iterable as $key => $value) {
108 5
            $separator = "\n";
109 5
            $valueIndent = $indent + self::INDENT;
110 5
            if (is_scalar($value) || $value instanceof Compact || $value instanceof \DateTime ) {
111 5
                $separator   = ' ';
112 5
                $valueIndent = 0;
113
            }
114 5
            $pairs[] = str_repeat(' ', $indent).sprintf($keyMask, $key).$separator.$this->dump($value, $valueIndent);
115
            //var_dump(str_repeat(' ', $indent)."key($keyMask):$key");
116
        }
117
        //var_dump($pairs);
118 5
        return implode("\n", $pairs);
119
    }
120
121
    /**
122
     * Dumps a Compact|mixed (representing an array or object) as the single-line format representation.
123
     * All values inside are assumed single-line as well.
124
     * Note: can NOT use JSON_encode because of possible reference calls or definitions as : '&abc 123', '*fre'
125
     * which would be quoted by json_encode
126
     *
127
     * @param mixed   $subject The subject
128
     * @param integer $indent  The indent
129
     *
130
     * @return string the string representation (JSON like) of the value
131
     */
132 2
    public function dumpCompact($subject, int $indent):string
133
    {
134 2
        $pairs = [];
135 2
        if (is_array($subject) || $subject instanceof \ArrayIterator) {
136 2
            $max = count($subject);
137 2
            $objectAsArray = is_array($subject) ? $subject : $subject->getArrayCopy();
138 2
            if(array_keys($objectAsArray) !== range(0, $max-1)) {
139 1
                $pairs = $objectAsArray;
140
            } else {
141 2
                $valuesList = [];
142 2
                foreach ($objectAsArray as $value) {
143 2
                    $valuesList[] = is_scalar($value) ? $this->dump($value, $indent) : $this->dumpCompact($value, $indent);
144
                }
145 2
                return '['.implode(', ', $valuesList).']';
146
            }
147
        } else {
148 1
            $pairs = get_object_vars($subject);
149
        }
150 2
        $content = [];
151 2
        foreach ($pairs as $key => $value) {
152 2
            $content[] = "$key: ".(is_scalar($value) ? $this->dump($value, $indent) : $this->dumpCompact($value, $indent));
153
        }
154 2
        return '{'.implode(', ', $content).'}';
155
    }
156
157
    /**
158
     * Dumps a string. Protects it if needed
159
     *
160
     * @param      string  $str    The string
161
     *
162
     * @return     string  ( description_of_the_return_value )
163
     * @todo   implements checking and protection function
164
     */
165 7
    public function dumpString(string $str):string
166
    {
167 7
        return ltrim($str);
168
    }
169
170 2
    public function dumpTagged(Tagged $obj, int $indent):string
171
    {
172 2
        $separator   = ' ';
173 2
        $valueIndent = 0;
174 2
        if (!is_scalar($obj->value)) {
175 1
            $separator = "\n";
176 1
            $valueIndent = $indent + self::INDENT;
177
        }
178 2
        return $obj->tagName.$separator.$this->dump($obj->value, $valueIndent);
179
    }
180
}
181