Completed
Pull Request — master (#20)
by Greg
03:00
created

FormatterManager::restructureData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 7
ccs 0
cts 0
cp 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 2
crap 6
1
<?php
2
namespace Consolidation\OutputFormatters;
3
4
use Symfony\Component\Console\Output\OutputInterface;
5
use Consolidation\OutputFormatters\Exception\UnknownFormatException;
6
use Consolidation\OutputFormatters\Formatters\RenderDataInterface;
7
8
/**
9
 * Manage a collection of formatters; return one on request.
10
 */
11
class FormatterManager
12
{
13
    protected $formatters = [];
14
15
    public function __construct()
16 28
    {
17
        $this->formatters = [
18 28
            'string' => '\Consolidation\OutputFormatters\Formatters\StringFormatter',
19 28
            'yaml' => '\Consolidation\OutputFormatters\Formatters\YamlFormatter',
20 28
            'json' => '\Consolidation\OutputFormatters\Formatters\JsonFormatter',
21 28
            'print-r' => '\Consolidation\OutputFormatters\Formatters\PrintRFormatter',
22 28
            'php' => '\Consolidation\OutputFormatters\Formatters\SerializeFormatter',
23 28
            'var_export' => '\Consolidation\OutputFormatters\Formatters\VarExportFormatter',
24 28
            'list' => '\Consolidation\OutputFormatters\Formatters\ListFormatter',
25 28
            'csv' => '\Consolidation\OutputFormatters\Formatters\CsvFormatter',
26 28
            'table' => '\Consolidation\OutputFormatters\Formatters\TableFormatter',
27 28
            'sections' => '\Consolidation\OutputFormatters\Formatters\SectionsFormatter',
28
        ];
29
30
        // Make the empty format an alias for the 'string' formatter.
31 28
        $this->formatters[''] = $this->formatters['string'];
32 28
    }
33
34
    /**
35
     * Add a formatter
36
     *
37
     * @param FormatterInterface $formatter the formatter to add
38
     * @return FormatterManager
39
     */
40
    public function add(FormatterInterface $formatter)
41
    {
42
        $this->formatters[] = $formatter;
43 28
        return $this;
44
    }
45 28
46 27
    /**
47 24
     * Return the identifiers for all valid data types that have been registered.
48 24
     *
49
     * @param mixed $dataType \ReflectionObject or other description of the produced data type
50 27
     * @return array
51
     */
52
    public function validFormats($dataType)
53
    {
54 27
        $validFormats = [];
55 27
        foreach ($this->formatters as $formatId => $formatterName) {
56 4
            $formatter = $this->getFormatter($formatId);
57
            if ($this->isValidFormat($formatter, $dataType)) {
58
                $validFormats[] = $formatId;
59
            }
60 27
        }
61
        return $validFormats;
62
    }
63 27
64
    public function isValidFormat(FormatterInterface $formatter, $dataType)
65
    {
66
        if (is_array($dataType)) {
67 24
            $dataType = new \ReflectionClass('\ArrayObject');
68
        }
69 24
        if (!$dataType instanceof \ReflectionClass) {
70
            $dataType = new \ReflectionClass($dataType);
71
        }
72
        // If the formatter does not implement ValidationInterface, then
73
        // it is presumed that the formatter only accepts arrays.
74
        if (!$formatter instanceof ValidationInterface) {
75
            return $dataType->isSubclassOf('ArrayObject') || ($dataType->getName() == 'ArrayObject');
76
        }
77
        $supportedTypes = $formatter->validDataTypes();
78
        foreach ($supportedTypes as $supportedType) {
79 28
            if (($dataType->getName() == $supportedType->getName()) || $dataType->isSubclassOf($supportedType->getName())) {
80
                return true;
81 28
            }
82 1
        }
83
        return false;
84
    }
85 27
86 27
    /**
87 9
     * Format and write output
88 9
     *
89 27
     * @param OutputInterface $output Output stream to write to
90
     * @param string $format Data format to output in
91
     * @param mixed $structuredOutput Data to output
92 28
     * @param FormatterOptions $options Formatting options
93
     */
94 28
    public function write(OutputInterface $output, $format, $structuredOutput, FormatterOptions $options)
95
    {
96
        $formatter = $this->getFormatter((string)$format);
97
        $structuredOutput = $this->validateAndRestructure($formatter, $structuredOutput, $options);
98
        $formatter->write($output, $structuredOutput, $options);
99
    }
100
101
102
103
    protected function validateAndRestructure(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
104
    {
105
        // Give the formatter a chance to do something with the
106
        // raw data before it is restructured.
107 24
        $overrideRestructure = $this->overrideRestructure($formatter, $structuredOutput, $options);
108
        if ($overrideRestructure) {
109 24
            return $overrideRestructure;
110 14
        }
111
112 14
        // Restructure the output data (e.g. select fields to display, etc.).
113
        $restructuredOutput = $this->restructureData($structuredOutput, $options);
114
115
        // Make sure that the provided data is in the correct format for the selected formatter.
116
        $restructuredOutput = $this->validateData($formatter, $restructuredOutput);
117
118
        // Give the original data a chance to re-render the structured
119
        // output after it has been restructured and validated.
120
        $restructuredOutput = $this->renderData($formatter, $structuredOutput, $restructuredOutput, $options);
121
122 27
        return $restructuredOutput;
123
    }
124
125
    /**
126 27
     * Fetch the requested formatter.
127 16
     *
128
     * @param string $format Identifier for requested formatter
129
     * @return FormatterInterface
130
     */
131
    public function getFormatter($format)
132 15
    {
133 4
        if (!$this->hasFormatter($format)) {
134
            throw new UnknownFormatException($format);
135
        }
136 11
        $formatter = new $this->formatters[$format];
137
        return $formatter;
138
    }
139
140
    /**
141
     * Test to see if the stipulated format exists
142
     */
143
    public function hasFormatter($format)
144
    {
145
        return array_key_exists($format, $this->formatters);
146
    }
147 27
148
    /**
149 27
     * Render the data as necessary (e.g. to select or reorder fields).
150 7
     *
151
     * @param FormatterInterface $formatter
152 20
     * @param mixed $originalData
153
     * @param mixed $restructuredData
154
     * @param FormatterOptions $options Formatting options
155
     * @return mixed
156
     */
157
    public function renderData(FormatterInterface $formatter, $originalData, $restructuredData, FormatterOptions $options)
158
    {
159
        if ($formatter instanceof RenderDataInterface) {
160
            return $formatter->renderData($originalData, $restructuredData, $options);
161
        }
162
        return $restructuredData;
163
    }
164
165
    /**
166
     * Determine if the provided data is compatible with the formatter being used.
167
     *
168 27
     * @param FormatterInterface $formatter Formatter being used
169
     * @param mixed $structuredOutput Data to validate
170 27
     * @return mixed
171 5
     */
172
    public function validateData(FormatterInterface $formatter, $structuredOutput)
173 26
    {
174
        // If the formatter implements ValidationInterface, then let it
175
        // test the data and throw or return an error
176
        if ($formatter instanceof ValidationInterface) {
177
            return $formatter->validate($structuredOutput);
178
        }
179
        // If the formatter does not implement ValidationInterface, then
180
        // it will never be passed an ArrayObject; we will always give
181
        // it a simple array.
182
        if ($structuredOutput instanceof \ArrayObject) {
183
            return $structuredOutput->getArrayCopy();
184
        }
185
        // If the formatter does not implmeent ValidationInterface, then
186
        // we will further presume that it will *only* accept arrays.
187
        if (!is_array($structuredOutput)) {
188
            throw new IncompatibleDataException(
189
                $formatter,
190
                $structuredOutput,
191
                []
192
            );
193
        }
194
        return $structuredOutput;
195
    }
196
197
    /**
198
     * Restructure the data as necessary (e.g. to select or reorder fields).
199
     *
200
     * @param mixed $structuredOutput
201
     * @param FormatterOptions $options
202
     * @return mixed
203
     */
204
    public function restructureData($structuredOutput, FormatterOptions $options)
205
    {
206
        if ($structuredOutput instanceof RestructureInterface) {
207
            return $structuredOutput->restructure($options);
208
        }
209
        return $structuredOutput;
210
    }
211
212
    /**
213
     * Allow the formatter access to the raw structured data prior
214
     * to restructuring.  For example, the 'list' formatter may wish
215
     * to display the row keys when provided table output.  If this
216
     * function returns a result that does not evaluate to 'false',
217
     * then that result will be used as-is, and restructuring and
218
     * validation will not occur.
219
     *
220
     * @param mixed $structuredOutput
221
     * @param FormatterOptions $options
222
     * @return mixed
223
     */
224
    public function overrideRestructure(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
225
    {
226
        if ($formatter instanceof OverrideRestructureInterface) {
227
            return $formatter->overrideRestructure($structuredOutput, $options);
228
        }
229
    }
230
}
231