Passed
Push — v5 ( 2ad962...88ce87 )
by Andrew
16:48 queued 13s
created

Code::icon()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
nc 1
nop 0
dl 0
loc 3
c 1
b 0
f 0
cc 1
rs 10
1
<?php
2
/**
3
 * Code Field plugin for Craft CMS
4
 *
5
 * Provides a Code Field that has a full-featured code editor with syntax highlighting & autocomplete
6
 *
7
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
8
 * @copyright Copyright (c) 2022 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
9
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
10
11
namespace nystudio107\codefield\fields;
12
13
use Craft;
14
use craft\base\ElementInterface;
0 ignored issues
show
Bug introduced by
The type craft\base\ElementInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use craft\base\Field;
16
use craft\base\PreviewableFieldInterface;
17
use craft\helpers\Html;
18
use craft\helpers\Json;
19
use craft\validators\ArrayValidator;
20
use GraphQL\Type\Definition\Type;
21
use nystudio107\codefield\gql\types\generators\CodeDataGenerator;
22
use nystudio107\codefield\models\CodeData;
23
use nystudio107\codefield\validators\JsonValidator;
24
use yii\db\Schema;
25
use function is_array;
26
use function is_string;
27
28
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
29
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
30
 * @package   CodeField
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
31
 * @since     4.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
32
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
33
class Code extends Field implements PreviewableFieldInterface
34
{
35
    // Constants
36
    // =========================================================================
37
38
    /**
39
     * Properties that are no longer used, but may still be passed into the $config
40
     * via __construct() that we should remove to avoid throwing an exception
41
     */
42
    protected const DEPRECATED_PROPERTIES = [
43
        'columnType'
44
    ];
45
46
    // Public Properties
47
    // =========================================================================
48
49
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
50
     * @var string The theme to use for the Code Editor field.
51
     */
52
    public string $theme = 'auto';
53
54
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
55
     * @var string The language to use for the Code Editor field.
56
     */
57
    public string $language = 'javascript';
58
59
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
60
     * @var bool Whether the Code Editor field display as a single line
61
     */
62
    public bool $singleLineEditor = false;
63
64
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
65
     * @var int The font size to use for the Code Editor field
66
     */
67
    public int $fontSize = 14;
68
69
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
70
     * @var bool Whether line numbers should be displayed in the Code Editor field
71
     */
72
    public bool $lineNumbers = false;
73
74
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
75
     * @var bool Whether code folding should be used in the Code Editor field
76
     */
77
    public bool $codeFolding = false;
78
79
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
80
     * @var string The text that will be shown if the code field is empty.
81
     */
82
    public string $placeholder = '';
83
84
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
85
     * @var bool Whether the language selector dropdown menu should be displayed.
86
     */
87
    public bool $showLanguageDropdown = true;
88
89
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
90
     * @var string The default value the Code Field will be populated with
91
     */
92
    public string $defaultValue = '';
93
94
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
95
     * @var array The languages that should be listed in the language selector dropdown menu.
96
     */
97
    public array $availableLanguages = [
98
        'css',
99
        'graphql',
100
        'html',
101
        'javascript',
102
        'json',
103
        'markdown',
104
        'mysql',
105
        'php',
106
        'shell',
107
        'twig',
108
        'typescript',
109
        'yaml',
110
    ];
111
112
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
113
     * @var string JSON blob of Monaco [EditorOptions](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IEditorOptions.html) that will override the default settings
114
     */
115
    public string $monacoEditorOptions = '';
116
117
    // Static Methods
118
    // =========================================================================
119
120
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $config should have a doc-comment as per coding-style.
Loading history...
121
     * @inheritdoc
122
     */
123
    public function __construct(array $config = [])
124
    {
125
        // Unset any deprecated properties
126
        foreach (self::DEPRECATED_PROPERTIES as $deprecatedProperty) {
127
            unset($config[$deprecatedProperty]);
128
        }
129
        parent::__construct($config);
130
    }
131
132
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
133
     * @inheritdoc
134
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
135
    public static function dbType(): array|string|null
136
    {
137
        return Schema::TYPE_TEXT;
138
    }
139
140
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
141
     * @inheritdoc
142
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
143
    public static function icon(): string
144
    {
145
        return 'code';
146
    }
147
148
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
149
     * @inheritdoc
150
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
151
    public static function displayName(): string
152
    {
153
        return Craft::t('codefield', 'Code');
154
    }
155
156
    // Public Methods
157
    // =========================================================================
158
159
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
160
     * @inheritdoc
161
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
162
    public function normalizeValue(mixed $value, ?ElementInterface $element = null): mixed
163
    {
164
        if ($value instanceof CodeData) {
165
            return $value;
166
        }
167
        // Default config
168
        $config = [
169
            'value' => $this->defaultValue,
170
            'language' => $this->language,
171
        ];
172
        // Handle incoming values potentially being JSON or an array
173
        if (!empty($value)) {
174
            // Handle JSON-encoded values coming in
175
            if (is_string($value)) {
176
                $jsonValue = Json::decodeIfJson($value);
177
                // If this is still a string (meaning it's not valid JSON), treat it as the value
178
                if (is_string($jsonValue)) {
179
                    $config['value'] = $jsonValue;
180
                }
181
                if (is_array($jsonValue)) {
182
                    // Check to make sure the array returned is an encoded `CodeData` config, with exactly
183
                    // the same expected key/value pairs
184
                    if (!array_diff_key($config, $jsonValue) && !array_diff_key($jsonValue, $config)) {
185
                        $value = $jsonValue;
186
                    } else {
187
                        // Otherwise treat it as JSON data
188
                        $value = [
189
                            'value' => $value,
190
                            'language' => 'json',
191
                        ];
192
                    }
193
                }
194
            }
195
            if (is_array($value)) {
196
                $config = array_merge($config, array_filter($value));
197
            }
198
        }
199
        // Create and validate the model
200
        $codeData = new CodeData($config);
201
        if (!$codeData->validate()) {
202
            Craft::error(
203
                Craft::t('codefield', 'CodeData failed validation: ')
204
                . print_r($codeData->getErrors(), true),
0 ignored issues
show
Bug introduced by
Are you sure print_r($codeData->getErrors(), true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

204
                . /** @scrutinizer ignore-type */ print_r($codeData->getErrors(), true),
Loading history...
205
                __METHOD__
206
            );
207
        }
208
209
        return $codeData;
210
    }
211
212
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
213
     * @inheritdoc
214
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
215
    public function getSettingsHtml(): ?string
216
    {
217
        $monacoLanguages = require(__DIR__ . '/MonacoLanguages.php');
0 ignored issues
show
Coding Style introduced by
"require" is a statement not a function; no parentheses are required
Loading history...
Coding Style introduced by
File is being conditionally included; use "include" instead
Loading history...
218
        $schemaFilePath = Craft::getAlias('@nystudio107/codefield/resources/IEditorOptionsSchema.json');
219
        $optionsSchema = @file_get_contents($schemaFilePath) ?: '';
0 ignored issues
show
Bug introduced by
It seems like $schemaFilePath can also be of type false; however, parameter $filename of file_get_contents() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

219
        $optionsSchema = @file_get_contents(/** @scrutinizer ignore-type */ $schemaFilePath) ?: '';
Loading history...
220
        // Render the settings template
221
        return Craft::$app->getView()->renderTemplate(
222
            'codefield/_components/fields/Code_settings',
223
            [
224
                'field' => $this,
225
                'monacoLanguages' => $monacoLanguages,
226
                'optionsSchema' => $optionsSchema,
227
            ]
228
        );
229
    }
230
231
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
232
     * @inheritdoc
233
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
234
    public function getInputHtml($value, ElementInterface $element = null): string
235
    {
236
        $twigVariables = $this->getFieldRenderVariables($value, $element, true);
0 ignored issues
show
Bug introduced by
It seems like $element can also be of type null; however, parameter $element of nystudio107\codefield\fi...tFieldRenderVariables() does only seem to accept craft\base\ElementInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

236
        $twigVariables = $this->getFieldRenderVariables($value, /** @scrutinizer ignore-type */ $element, true);
Loading history...
237
        // Render the input template
238
        return Craft::$app->getView()->renderTemplate(
239
            'codefield/_components/fields/Code_input',
240
            $twigVariables
241
        );
242
    }
243
244
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
245
     * @inheritdoc
246
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
247
    public function getStaticHtml(mixed $value, ElementInterface $element): string
248
    {
249
        $twigVariables = $this->getFieldRenderVariables($value, $element, false);
250
        // Render the input template
251
        return Craft::$app->getView()->renderTemplate(
252
            'codefield/_components/fields/Code_input',
253
            $twigVariables
254
        );
255
    }
256
257
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
258
     * @inheritdoc
259
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
260
    public function getContentGqlType(): Type|array
261
    {
262
        $typeArray = CodeDataGenerator::generateTypes($this);
263
264
        return [
265
            'name' => $this->handle,
266
            'description' => 'Code Editor field',
267
            'type' => array_shift($typeArray),
268
        ];
269
    }
270
271
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
272
     * @inheritdoc
273
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
274
    public function rules(): array
275
    {
276
        $rules = parent::rules();
277
        return array_merge($rules, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
278
            ['theme', 'in', 'range' => ['auto', 'vs', 'vs-dark', 'hc-black']],
279
            ['theme', 'default', 'value' => 'auto'],
280
            ['language', 'string'],
281
            ['language', 'default', 'value' => 'javascript'],
282
            [['singleLineEditor', 'showLanguageDropdown', 'lineNumbers', 'codeFolding'], 'boolean'],
283
            ['placeholder', 'string'],
284
            ['placeholder', 'default', 'value' => ''],
285
            ['fontSize', 'integer'],
286
            ['fontSize', 'default', 'value' => 14],
287
            ['availableLanguages', ArrayValidator::class],
288
            ['monacoEditorOptions', JsonValidator::class],
289
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
290
    }
291
292
    // Protected Methods
293
    // =========================================================================
294
295
    /**
296
     * Return the Twig variables for rendering the field
297
     *
298
     * @param $value
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
299
     * @param ElementInterface $element
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
300
     * @param bool $enabled Whether the field is enabled or not
0 ignored issues
show
Coding Style introduced by
Expected 13 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
301
     * @return array[]
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
302
     */
303
    protected function getFieldRenderVariables($value, ElementInterface $element, bool $enabled): array
304
    {
305
        // Get our id and namespace
306
        $id = Html::id($this->handle);
307
        $namespacedId = Craft::$app->getView()->namespaceInputId($id);
308
309
        // Extract just the languages that have been selected for display
310
        $displayLanguages = [];
311
        if ($this->showLanguageDropdown) {
312
            $monacoLanguages = require(__DIR__ . '/MonacoLanguages.php');
0 ignored issues
show
Coding Style introduced by
"require" is a statement not a function; no parentheses are required
Loading history...
Coding Style introduced by
File is being conditionally included; use "include" instead
Loading history...
313
            $decomposedLanguages = array_column($monacoLanguages, 'label', 'value');
314
            $displayLanguages = array_intersect_key($decomposedLanguages, array_flip($this->availableLanguages));
315
            // Handle "all" checkbox
316
            if ($this->availableLanguages[0] === '*') {
317
                $displayLanguages = $decomposedLanguages;
318
            }
319
            $displayLanguages = array_map(static function($k, $v) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
320
                return ['value' => $k, 'label' => $v];
321
            }, array_keys($displayLanguages), array_values($displayLanguages));
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
322
        }
323
        $monacoOptionsOverride = Json::decodeIfJson($this->monacoEditorOptions);
324
        if ($monacoOptionsOverride === null || is_string($monacoOptionsOverride)) {
325
            $monacoOptionsOverride = [];
326
        }
327
        // Disable the Monaco editor
328
        if (!$enabled) {
329
            $monacoOptionsOverride['domReadOnly'] = true;
330
            $monacoOptionsOverride['readOnly'] = true;
331
        }
332
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('name' => $...$monacoOptionsOverride) returns an array which contains values of type nystudio107\codefield\fields\Code|string which are incompatible with the documented value type array.
Loading history...
333
            'name' => $this->handle,
334
            'value' => $value,
335
            'field' => $this,
336
            'orientation' => $this->getOrientation($element),
337
            'id' => $id,
338
            'namespacedId' => $namespacedId,
339
            'displayLanguages' => $displayLanguages,
340
            'monacoOptionsOverride' => $monacoOptionsOverride,
341
        ];
342
    }
343
}
344