Completed
Push — master ( d81ab2...154df5 )
by stéphane
06:42 queued 13s
created

DumperHandlers::dumpCompound()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.0187

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 4
nop 2
dl 0
loc 14
ccs 10
cts 11
cp 0.9091
crap 5.0187
rs 9.6111
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
        if (is_array($compound)) {
63 3
            $iterator = new \ArrayIterator($compound);
64 3
            $mask = '-';
65 3
            $refKeys = range(0, count($compound)-1);
66 3
            if (array_keys($compound) !== $refKeys) {
67
                $mask = '%s:';
68
            }
69 3
            return $this->iteratorToString($iterator, $mask, $indent);
70 3
        } elseif (is_object($compound) && !is_callable($compound)) {
71 2
            return $this->dumpObject($compound, $indent);
72
        }
73 1
        throw new \Exception("Dumping Callable|Resource is not currently supported", 1);
74
    }
75
76 2
    private function dumpObject(object $object, int $indent):string
77
    {
78 2
        if ($object instanceof YamlObject) {
79 1
            return $this->dumpYamlObject($object);
80 2
        } elseif ($object instanceof Compact) {
81 1
            return $this->dumpCompact($object, $indent);
82 2
        } elseif ($object instanceof Tagged) {
83 1
            return $this->dumpTagged($object, $indent);
84 1
        } elseif ($object instanceof \DateTime) {
85
            return $object->format(self::DATE_FORMAT);
86 1
        } elseif (is_iterable($object)) {
87
            $iterator = $object;
88
        } else {
89 1
            $iterator = new \ArrayIterator(get_object_vars($object));
90
        }
91 1
        return $this->iteratorToString($iterator, '%s:', $indent);
92
    }
93
94
95 2
    private function dumpYamlObject(YamlObject $obj):string
96
    {
97 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

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