Issues (2)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Eloquent/Extensions/TranslateableTrait.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace RichanFongdasen\I18n\Eloquent\Extensions;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Relations\HasMany;
8
use Illuminate\Support\Str;
9
use RichanFongdasen\I18n\Eloquent\Observer;
10
use RichanFongdasen\I18n\Eloquent\TranslationModel;
11
use RichanFongdasen\I18n\Eloquent\TranslationScope;
12
use RichanFongdasen\I18n\Locale;
13
14
trait TranslateableTrait
15
{
16
    /**
17
     * Fallback translation object. The model will use
18
     * the value from this object, if there was no
19
     * translated attribute value for current locale.
20
     *
21
     * @var \RichanFongdasen\I18n\Eloquent\TranslationModel
22
     */
23
    protected $fallbackTranslation;
24
25
    /**
26
     * Current selected locale.
27
     *
28
     * @var \RichanFongdasen\I18n\Locale
29
     */
30
    protected $locale;
31
32
    /**
33
     * Default language key.
34
     *
35
     * @var string
36
     */
37
    protected static $localeKey;
38
39
    /**
40
     * Translation object for the current selected
41
     * locale.
42
     *
43
     * @var \RichanFongdasen\I18n\Eloquent\TranslationModel
44
     */
45
    protected $translation;
46
47
    /**
48
     * Convert the model's attributes to an array.
49
     *
50
     * @return array
51
     */
52
    public function attributesToArray(): array
53
    {
54
        $attributes = parent::attributesToArray();
55
56
        foreach ($this->getTranslateableAttributes() as $key) {
57
            $attributes[$key] = $this->getAttribute($key);
58
        }
59
60
        return $attributes;
61
    }
62
63
    /**
64
     * Boot the TranslateableTrait model extension.
65
     *
66
     * @return void
67
     */
68
    public static function bootTranslateableTrait(): void
69
    {
70
        static::addGlobalScope(new TranslationScope());
71
        static::observe(app(Observer::class));
72
        static::$localeKey = \I18n::getConfig('language_key');
73
    }
74
75
    /**
76
     * Create a new translation for the given locale.
77
     *
78
     * @param \RichanFongdasen\I18n\Locale $locale
79
     *
80
     * @return \RichanFongdasen\I18n\Eloquent\TranslationModel
81
     */
82
    protected function createTranslation(Locale $locale): TranslationModel
83
    {
84
        $conditions = [
85
            $this->getForeignKey() => $this->getKey(),
86
            'locale'               => $locale->{self::$localeKey},
87
        ];
88
89
        $model = (new TranslationModel())
90
            ->fill($conditions);
91
92
        $this->translations->push($model);
93
94
        return $model;
95
    }
96
97
    /**
98
     * Fill the model with an array of attributes.
99
     *
100
     * @param array $attributes
101
     *
102
     * @return $this
0 ignored issues
show
Should the return type not be \self?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
103
     */
104
    public function fill(array $attributes): self
105
    {
106
        foreach ($this->getTranslateableAttributes() as $key) {
107
            if (isset($attributes[$key])) {
108
                $this->setTranslateableAttribute($key, $attributes[$key]);
109
            }
110
        }
111
112
        return parent::fill($attributes);
113
    }
114
115
    /**
116
     * Get an attribute from the model.
117
     *
118
     * @param string $key
119
     *
120
     * @return mixed
121
     */
122
    public function getAttribute($key)
123
    {
124
        if ($this->isTranslateableAttribute($key)) {
125
            return $this->getTranslated($key);
126
        }
127
128
        return parent::getAttribute($key);
129
    }
130
131
    /**
132
     * Get join table attributes.
133
     *
134
     * @return string[]
135
     */
136
    protected function getJoinAttributes(): array
137
    {
138
        $attributes = [$this->getTable().'.*'];
139
140
        foreach ($this->getTranslateableAttributes() as $attribute) {
141
            $attributes[] = $this->getTranslationTable().'.'.$attribute;
142
        }
143
144
        return $attributes;
145
    }
146
147
    /**
148
     * Get all of translateable attributes.
149
     *
150
     * @return array
151
     */
152
    public function getTranslateableAttributes(): array
153
    {
154
        return is_array($this->translateFields) ? $this->translateFields : [];
155
    }
156
157
    /**
158
     * Get a translated attribute value from
159
     * the model.
160
     *
161
     * @param string $key
162
     *
163
     * @return mixed
164
     */
165
    protected function getTranslated(string $key)
166
    {
167
        if (!$this->locale) {
168
            $this->translate();
169
        }
170
171
        if ($result = $this->getTranslatedValue($this->translation, $key)) {
172
            return $result;
173
        }
174
175
        return $this->getTranslatedValue($this->fallbackTranslation, $key);
176
    }
177
178
    /**
179
     * Get a translated attribute value from
180
     * the given translation model.
181
     *
182
     * @param mixed  $translation
183
     * @param string $key
184
     *
185
     * @return mixed
186
     */
187
    protected function getTranslatedValue($translation, string $key)
188
    {
189
        if (!$translation instanceof Model) {
190
            return null;
191
        }
192
193
        return $translation->getAttribute($key);
194
    }
195
196
    /**
197
     * Get existing translation or create a
198
     * new one.
199
     *
200
     * @param \RichanFongdasen\I18n\Locale $locale
201
     *
202
     * @return \RichanFongdasen\I18n\Eloquent\TranslationModel
203
     */
204
    protected function getTranslation(Locale $locale): TranslationModel
205
    {
206
        $this->translate($locale);
207
208
        if ($this->translation) {
209
            return $this->translation;
210
        }
211
212
        return $this->translation = $this->createTranslation($locale);
213
    }
214
215
    /**
216
     * Find locale object based on the given
217
     * key value.
218
     *
219
     * @param mixed $key
220
     *
221
     * @return \RichanFongdasen\I18n\Locale
222
     */
223
    protected function getTranslationLocale($key = null): Locale
224
    {
225
        if ($key instanceof Locale) {
226
            return $key;
227
        }
228
229
        if (empty($key)) {
230
            $key = \App::getLocale();
231
        }
232
233
        if (!$locale = \I18n::getLocale($key)) {
234
            $locale = \I18n::defaultLocale();
235
        }
236
237
        return $locale;
238
    }
239
240
    /**
241
     * Get translation table.
242
     *
243
     * @return string
244
     */
245
    public function getTranslationTable(): string
246
    {
247
        if (!isset($this->translationTable)) {
248
            $suffix = \I18n::getConfig('translation_table_suffix');
249
250
            return Str::snake(class_basename($this)).'_'.$suffix;
251
        }
252
253
        return $this->translationTable;
254
    }
255
256
    /**
257
     * Check whether the given attribute key is
258
     * translateable.
259
     *
260
     * @param string $key
261
     *
262
     * @return bool
263
     */
264
    protected function isTranslateableAttribute(string $key): bool
265
    {
266
        $fields = $this->getTranslateableAttributes();
267
268
        return in_array($key, $fields);
269
    }
270
271
    /**
272
     * Add and additional scope to join the translation table
273
     * and make the translation content more easier to search.
274
     *
275
     * @param \Illuminate\Database\Eloquent\Builder $query
276
     *
277
     * @return \Illuminate\Database\Eloquent\Builder
278
     */
279
    public function scopeJoinTranslation(Builder $query): Builder
280
    {
281
        $attributes = $this->getJoinAttributes();
282
283
        return $query->leftJoin(
284
            $this->getTranslationTable(),
285
            $this->getTable().'.'.$this->getKeyName(),
286
            '=',
287
            $this->getTranslationTable().'.'.$this->getForeignKey()
288
        )->select($attributes)
289
        ->where($this->getTranslationTable().'.locale', \App::getLocale());
290
    }
291
292
    /**
293
     * Set a given attribute on the model.
294
     *
295
     * @param string $key
296
     * @param mixed  $value
297
     *
298
     * @return mixed
299
     */
300
    public function setAttribute($key, $value)
301
    {
302
        if ($this->isTranslateableAttribute($key)) {
303
            if (!$this->locale) {
304
                $this->translate();
305
            }
306
            if (!$this->translation instanceof TranslationModel) {
307
                $this->translation = $this->getTranslation($this->locale);
308
            }
309
310
            $this->translation->setAttribute($key, $value);
311
            $this->updateTimestamps();
312
313
            return $this;
314
        }
315
316
        return parent::setAttribute($key, $value);
317
    }
318
319
    /**
320
     * Set fallback translation model.
321
     *
322
     * @return void
323
     */
324
    protected function setFallbackTranslation(): void
325
    {
326
        $locale = \I18n::defaultLocale()->{self::$localeKey};
327
        $this->fallbackTranslation = $this->translations->where('locale', $locale)->first();
328
    }
329
330
    /**
331
     * Set translateable attribute based on the
332
     * given key.
333
     *
334
     * @param string $key
335
     * @param mixed  $data
336
     * @param mixed  $locale
337
     *
338
     * @return $this
339
     */
340
    protected function setTranslateableAttribute(string $key, $data, $locale = null): self
341
    {
342
        if (is_array($data)) {
343
            foreach ($data as $language => $value) {
344
                $this->setTranslateableAttribute($key, $value, $language);
345
            }
346
347
            return $this;
348
        }
349
        if (!$locale && $this->locale) {
350
            $locale = $this->locale;
351
        }
352
        $this->translate($locale);
353
        $this->setAttribute($key, $data);
354
355
        return $this;
356
    }
357
358
    /**
359
     * Translate current model.
360
     *
361
     * @param mixed $key
362
     *
363
     * @return $this
364
     */
365
    public function translate($key = null): self
366
    {
367
        if (!$this->fallbackTranslation) {
368
            $this->setFallbackTranslation();
369
        }
370
371
        $this->locale = $this->getTranslationLocale($key);
372
373
        $key = $this->locale->{self::$localeKey};
374
        $this->translation = $this->translations->where('locale', $key)->first();
375
376
        return $this;
377
    }
378
379
    /**
380
     * Define HasMany model relationship
381
     * with its translation model.
382
     *
383
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
384
     */
385
    public function translations(): HasMany
386
    {
387
        $model = new TranslationModel();
388
        $model->setTable($this->getTranslationTable());
389
390
        return new HasMany(
391
            $model->newQuery(),
392
            $this,
393
            $this->getForeignKey(),
394
            $this->getKeyName()
395
        );
396
    }
397
398
    /**
399
     * Get the default foreign key name for the model.
400
     *
401
     * @return string
402
     */
403
    abstract public function getForeignKey();
404
405
    /**
406
     * Get the value of the model's primary key.
407
     *
408
     * @return mixed
409
     */
410
    abstract public function getKey();
411
412
    /**
413
     * Get the primary key for the model.
414
     *
415
     * @return string
416
     */
417
    abstract public function getKeyName();
418
419
    /**
420
     * Get the table associated with the model.
421
     *
422
     * @return string
423
     */
424
    abstract public function getTable();
425
426
    /**
427
     * Update the creation and update timestamps.
428
     *
429
     * @return void
430
     */
431
    abstract protected function updateTimestamps();
432
}
433