Completed
Push — master ( 3c952e...cb21a4 )
by Greg
11s
created

FormatterManager::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 1

Importance

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