Completed
Pull Request — master (#77)
by Greg
01:42
created

FormatterOptions::removeOption()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
namespace Consolidation\OutputFormatters\Options;
3
4
use Symfony\Component\Console\Input\InputInterface;
5
use Symfony\Component\Console\Output\OutputInterface;
6
7
use Consolidation\OutputFormatters\Transformations\PropertyParser;
8
use Consolidation\OutputFormatters\StructuredData\Xml\XmlSchema;
9
use Consolidation\OutputFormatters\StructuredData\Xml\XmlSchemaInterface;
10
use Symfony\Component\Console\Output\ConsoleOutput;
11
12
/**
13
 * FormetterOptions holds information that affects the way a formatter
14
 * renders its output.
15
 *
16
 * There are three places where a formatter might get options from:
17
 *
18
 * 1. Configuration associated with the command that produced the output.
19
 *    This is passed in to FormatterManager::write() along with the data
20
 *    to format.  It might originally come from annotations on the command,
21
 *    or it might come from another source.  Examples include the field labels
22
 *    for a table, or the default list of fields to display.
23
 *
24
 * 2. Options specified by the user, e.g. by commandline options.
25
 *
26
 * 3. Default values associated with the formatter itself.
27
 *
28
 * This class caches configuration from sources (1) and (2), and expects
29
 * to be provided the defaults, (3), whenever a value is requested.
30
 */
31
class FormatterOptions
32
{
33
    /** var array */
34
    protected $configurationData = [];
35
    /** var array */
36
    protected $options = [];
37
    /** var InputInterface */
38
    protected $input;
39
40
    const FORMAT = 'format';
41
    const DEFAULT_FORMAT = 'default-format';
42
    const TABLE_STYLE = 'table-style';
43
    const LIST_ORIENTATION = 'list-orientation';
44
    const FIELDS = 'fields';
45
    const FIELD = 'field';
46
    const INCLUDE_FIELD_LABELS = 'include-field-labels';
47
    const ROW_LABELS = 'row-labels';
48
    const FIELD_LABELS = 'field-labels';
49
    const DEFAULT_FIELDS = 'default-fields';
50
    const DEFAULT_STRING_FIELD = 'default-string-field';
51
    const DELIMITER = 'delimiter';
52
    const LIST_DELIMITER = 'list-delimiter';
53
    const TERMINAL_WIDTH = 'width';
54
    const METADATA_TEMPLATE = 'metadata-template';
55
56
    /**
57
     * Create a new FormatterOptions with the configuration data and the
58
     * user-specified options for this request.
59
     *
60
     * @see FormatterOptions::setInput()
61
     * @param array $configurationData
62
     * @param array $options
63
     */
64
    public function __construct($configurationData = [], $options = [])
65
    {
66
        $this->configurationData = $configurationData;
67
        $this->options = $options;
68
    }
69
70
    /**
71
     * Create a new FormatterOptions object with new configuration data (provided),
72
     * and the same options data as this instance.
73
     *
74
     * @param array $configurationData
75
     * @return FormatterOptions
76
     */
77
    public function override($configurationData)
78
    {
79
        $override = new self();
80
        $override
81
            ->setConfigurationData($configurationData + $this->getConfigurationData())
82
            ->setOptions($this->getOptions());
83
        return $override;
84
    }
85
86
    public function setTableStyle($style)
87
    {
88
        return $this->setConfigurationValue(self::TABLE_STYLE, $style);
89
    }
90
91
    public function setDelimiter($delimiter)
92
    {
93
        return $this->setConfigurationValue(self::DELIMITER, $delimiter);
94
    }
95
96
    public function setListDelimiter($listDelimiter)
97
    {
98
        return $this->setConfigurationValue(self::LIST_DELIMITER, $listDelimiter);
99
    }
100
101
    public function setIncludeFieldLables($includFieldLables)
102
    {
103
        return $this->setConfigurationValue(self::INCLUDE_FIELD_LABELS, $includFieldLables);
104
    }
105
106
    public function setListOrientation($listOrientation)
107
    {
108
        return $this->setConfigurationValue(self::LIST_ORIENTATION, $listOrientation);
109
    }
110
111
    public function setRowLabels($rowLabels)
112
    {
113
        return $this->setConfigurationValue(self::ROW_LABELS, $rowLabels);
114
    }
115
116
    public function setDefaultFields($fields)
117
    {
118
        return $this->setConfigurationValue(self::DEFAULT_FIELDS, $fields);
119
    }
120
121
    public function setFieldLabels($fieldLabels)
122
    {
123
        return $this->setConfigurationValue(self::FIELD_LABELS, $fieldLabels);
124
    }
125
126
    public function setDefaultStringField($defaultStringField)
127
    {
128
        return $this->setConfigurationValue(self::DEFAULT_STRING_FIELD, $defaultStringField);
129
    }
130
131
    public function setWidth($width)
132
    {
133
        return $this->setConfigurationValue(self::TERMINAL_WIDTH, $width);
134
    }
135
136
    /**
137
     * Get a formatter option
138
     *
139
     * @param string $key
140
     * @param array $defaults
141
     * @param mixed $default
142
     * @return mixed
143
     */
144
    public function get($key, $defaults = [], $default = false)
145
    {
146
        $value = $this->fetch($key, $defaults, $default);
147
        return $this->parse($key, $value);
148
    }
149
150
    /**
151
     * Return the XmlSchema to use with --format=xml for data types that support
152
     * that.  This is used when an array needs to be converted into xml.
153
     *
154
     * @return XmlSchema
155
     */
156
    public function getXmlSchema()
157
    {
158
        return new XmlSchema();
159
    }
160
161
    /**
162
     * Determine the format that was requested by the caller.
163
     *
164
     * @param array $defaults
165
     * @return string
166
     */
167
    public function getFormat($defaults = [])
168
    {
169
        return $this->get(self::FORMAT, [], $this->get(self::DEFAULT_FORMAT, $defaults, ''));
170
    }
171
172
    /**
173
     * Look up a key, and return its raw value.
174
     *
175
     * @param string $key
176
     * @param array $defaults
177
     * @param mixed $default
178
     * @return mixed
179
     */
180
    protected function fetch($key, $defaults = [], $default = false)
181
    {
182
        $defaults = $this->defaultsForKey($key, $defaults, $default);
183
        $values = $this->fetchRawValues($defaults);
184
        return $values[$key];
185
    }
186
187
    /**
188
     * Reduce provided defaults to the single item identified by '$key',
189
     * if it exists, or an empty array otherwise.
190
     *
191
     * @param string $key
192
     * @param array $defaults
193
     * @return array
194
     */
195
    protected function defaultsForKey($key, $defaults, $default = false)
196
    {
197
        if (array_key_exists($key, $defaults)) {
198
            return [$key => $defaults[$key]];
199
        }
200
        return [$key => $default];
201
    }
202
203
    /**
204
     * Look up all of the items associated with the provided defaults.
205
     *
206
     * @param array $defaults
207
     * @return array
208
     */
209
    protected function fetchRawValues($defaults = [])
210
    {
211
        return array_merge(
212
            $defaults,
213
            $this->getConfigurationData(),
214
            $this->getOptions(),
215
            $this->getInputOptions($defaults)
216
        );
217
    }
218
219
    /**
220
     * Given the raw value for a specific key, do any type conversion
221
     * (e.g. from a textual list to an array) needed for the data.
222
     *
223
     * @param string $key
224
     * @param mixed $value
225
     * @return mixed
226
     */
227
    protected function parse($key, $value)
228
    {
229
        $optionFormat = $this->getOptionFormat($key);
230
        if (!empty($optionFormat) && is_string($value)) {
231
            return $this->$optionFormat($value);
232
        }
233
        return $value;
234
    }
235
236
    /**
237
     * Convert from a textual list to an array
238
     *
239
     * @param string $value
240
     * @return array
241
     */
242
    public function parsePropertyList($value)
243
    {
244
        return PropertyParser::parse($value);
245
    }
246
247
    /**
248
     * Given a specific key, return the class method name of the
249
     * parsing method for data stored under this key.
250
     *
251
     * @param string $key
252
     * @return string
253
     */
254
    protected function getOptionFormat($key)
255
    {
256
        $propertyFormats = [
257
            self::ROW_LABELS => 'PropertyList',
258
            self::FIELD_LABELS => 'PropertyList',
259
        ];
260
        if (array_key_exists($key, $propertyFormats)) {
261
            return "parse{$propertyFormats[$key]}";
262
        }
263
        return '';
264
    }
265
266
    /**
267
     * Change the configuration data for this formatter options object.
268
     *
269
     * @param array $configurationData
270
     * @return FormatterOptions
271
     */
272
    public function setConfigurationData($configurationData)
273
    {
274
        $this->configurationData = $configurationData;
275
        return $this;
276
    }
277
278
    /**
279
     * Change one configuration value for this formatter option.
280
     *
281
     * @param string $key
282
     * @param mixed $value
283
     * @return FormetterOptions
284
     */
285
    protected function setConfigurationValue($key, $value)
286
    {
287
        $this->configurationData[$key] = $value;
288
        return $this;
289
    }
290
291
    /**
292
     * Change one configuration value for this formatter option, but only
293
     * if it does not already have a value set.
294
     *
295
     * @param string $key
296
     * @param mixed $value
297
     * @return FormetterOptions
298
     */
299
    public function setConfigurationDefault($key, $value)
300
    {
301
        if (!array_key_exists($key, $this->configurationData)) {
302
            return $this->setConfigurationValue($key, $value);
303
        }
304
        return $this;
305
    }
306
307
    /**
308
     * Return a reference to the configuration data for this object.
309
     *
310
     * @return array
311
     */
312
    public function getConfigurationData()
313
    {
314
        return $this->configurationData;
315
    }
316
317
    /**
318
     * Set all of the options that were specified by the user for this request.
319
     *
320
     * @param array $options
321
     * @return FormatterOptions
322
     */
323
    public function setOptions($options)
324
    {
325
        $this->options = $options;
326
        return $this;
327
    }
328
329
    /**
330
     * Remove an option.
331
     */
332
    public function removeOption($key)
333
    {
334
        unset($this->options[$key]);
335
    }
336
337
    /**
338
     * Change one option value specified by the user for this request.
339
     *
340
     * @param string $key
341
     * @param mixed $value
342
     * @return FormatterOptions
343
     */
344
    public function setOption($key, $value)
345
    {
346
        $this->options[$key] = $value;
347
        return $this;
348
    }
349
350
    /**
351
     * Return a reference to the user-specified options for this request.
352
     *
353
     * @return array
354
     */
355
    public function getOptions()
356
    {
357
        return $this->options;
358
    }
359
360
    /**
361
     * Provide a Symfony Console InputInterface containing the user-specified
362
     * options for this request.
363
     *
364
     * @param InputInterface $input
365
     * @return type
366
     */
367
    public function setInput(InputInterface $input)
368
    {
369
        $this->input = $input;
370
    }
371
372
    /**
373
     * Return all of the options from the provided $defaults array that
374
     * exist in our InputInterface object.
375
     *
376
     * @param array $defaults
377
     * @return array
378
     */
379
    public function getInputOptions($defaults)
380
    {
381
        if (!isset($this->input)) {
382
            return [];
383
        }
384
        $options = [];
385
        foreach ($defaults as $key => $value) {
386
            if ($this->input->hasOption($key)) {
387
                $result = $this->input->getOption($key);
388
                if (isset($result)) {
389
                    $options[$key] = $this->input->getOption($key);
390
                }
391
            }
392
        }
393
        return $options;
394
    }
395
396
    public function shouldAppendNewline(OutputInterface $output)
397
    {
398
        return $this->fetch('append-eol', [], $this->defaultAppendEol($output));
399
    }
400
401
    protected function defaultAppendEol(OutputInterface $output)
402
    {
403
        // If we have a reference to the input object, and we are
404
        // not in interactive mode, then by default do not append an eol.
405
        if ($this->input && !$this->isInteractive()) {
406
            return false;
407
        }
408
409
        // If we are not writing to the console, then by default
410
        // do not append an eol.
411
        if (!$output instanceof ConsoleOutput) {
412
            return false;
413
        }
414
415
        // If we cannot tell whether output is being redirected or not,
416
        // assume that it is not.
417
        if (function_exists('posix_isatty')) {
418
            return true;
419
        }
420
421
        // If stdout is a tty, then add an EOL. Otherwise, do not.
422
        return posix_isatty(STDOUT);
423
    }
424
425
    /**
426
     * Check to see if we are in interactive mode. If we were never
427
     * given a reference to the input object, then assume not.
428
     */
429
    public function isInteractive()
430
    {
431
        if (!$this->input) {
432
            return false;
433
        }
434
        return $this->input->isInteractive();
435
    }
436
}
437