Completed
Push — master ( 7cc354...d81ab2 )
by stéphane
03:28
created

DumperHandlers   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Test Coverage

Coverage 94.25%

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 171
ccs 82
cts 87
cp 0.9425
rs 8.64
c 0
b 0
f 0
wmc 47

10 Methods

Rating   Name   Duplication   Size   Complexity  
B dumpScalar() 0 11 8
A dump() 0 10 4
A __construct() 0 3 2
A dumpYamlObject() 0 10 5
A iteratorToString() 0 13 5
A dumpCompound() 0 14 5
A dumpObject() 0 16 6
A dumpTagged() 0 9 2
A dumpString() 0 3 1
B dumpCompact() 0 23 9

How to fix   Complexity   

Complex Class

Complex classes like DumperHandlers often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DumperHandlers, and based on these observations, apply Extract Interface, too.

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_callable($compound)) {
63 1
            throw new \Exception("Dumping Callable|Closure is not currently supported", 1);
64 3
        } elseif (is_array($compound)) {
65 3
            $iterator = new \ArrayIterator($compound);
66 3
            $mask = '-';
67 3
            $refKeys = range(0, count($compound)-1);
68 3
            if (array_keys($compound) !== $refKeys) {
69
                $mask = '%s:';
70
            }
71 3
            return $this->iteratorToString($iterator, $mask, $indent);
72 2
        } elseif (is_object($compound)) {
73 2
            return $this->dumpObject($compound, $indent);
74
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
75
    }
76
77 2
    private function dumpObject(object $object, int $indent):string
78
    {
79 2
        if ($object instanceof YamlObject) {
80 1
            return $this->dumpYamlObject($object);
81 2
        } elseif ($object instanceof Compact) {
82 1
            return $this->dumpCompact($object, $indent);
83 2
        } elseif ($object instanceof Tagged) {
84 1
            return $this->dumpTagged($object, $indent);
85 1
        } elseif ($object instanceof \DateTime) {
86
            return $object->format(self::DATE_FORMAT);
87 1
        } elseif (is_iterable($object)) {
88
            $iterator = $object;
89
        } else {
90 1
            $iterator = new \ArrayIterator(get_object_vars($object));
91
        }
92 1
        return $this->iteratorToString($iterator, '%s:', $indent);
93
    }
94
95
96 2
    private function dumpYamlObject(YamlObject $obj):string
97
    {
98 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

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