Passed
Push — master ( 70f5fe...98d705 )
by Vasyl
01:46
created

StrTokenGenerator::configTokens()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: fomvasss
5
 * Date: 26.10.18
6
 * Time: 03:21
7
 */
8
9
namespace Fomvasss\LaravelStrTokens;
10
11
use App\Models\MetaTag;
0 ignored issues
show
Bug introduced by
The type App\Models\MetaTag 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...
12
use Carbon\Carbon;
13
use Illuminate\Database\Eloquent\Collection;
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\Collection 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...
14
use Illuminate\Database\Eloquent\Model;
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\Model 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
 
16
class StrTokenGenerator
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $text = '';
22
23
    /**
24
     * @var null
25
     */
26
    protected $date = null;
27
28
    /**
29
     * @var null
30
     */
31
    protected $entity = null;
32
33
    /**
34
     * @var bool
35
     */
36
    protected $clearEmptyTokens = true;
37
38
    /**
39
     * @param string $text
40
     * @return StrTokenGenerator
41
     */
42
    public function setText(string $text = ''): self
43
    {
44
        $this->text = $text;
45
46
        return $this;
47
    }
48
49
    /**
50
     * @param Carbon $date
51
     * @return StrTokenGenerator
52
     */
53
    public function setDate(Carbon $date): self
54
    {
55
        $this->date = $date;
0 ignored issues
show
Documentation Bug introduced by
It seems like $date of type Carbon\Carbon is incompatible with the declared type null of property $date.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
56
57
        return $this;
58
    }
59
60
    /**
61
     * @param Model $entity
62
     * @return StrTokenGenerator
63
     */
64
    public function setEntity(Model $entity): self
65
    {
66
        $this->entity = $entity;
0 ignored issues
show
Documentation Bug introduced by
It seems like $entity of type Illuminate\Database\Eloquent\Model is incompatible with the declared type null of property $entity.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
67
68
        return $this;
69
    }
70
71
    /**
72
     * @return StrTokenGenerator
73
     */
74
    public function doNotClearEmptyTokens(): self
75
    {
76
        $this->clearEmptyTokens = false;
77
78
        return $this;
79
    }
80
81
    /**
82
     * @return StrTokenGenerator
83
     */
84
    public function clearEmptyTokens(): self
85
    {
86
        $this->clearEmptyTokens = true;
87
88
        return $this;
89
    }
90
91
    /**
92
     * @return string
93
     */
94
    public function replace(): string
95
    {
96
        $groupTokens = $this->tokenScan($this->text);
97
        $replacements = [];
98
99
        foreach ($groupTokens as $key => $attributes) {
100
101
            if ($key === 'date') {
102
                $replacements += $this->dateTokens($attributes);
103
104
            } elseif ($key === 'config') {
105
                $replacements += $this->configTokens($attributes);
106
107
            } elseif ($key === 'var') {
108
                $replacements += $this->variablesTokens($attributes);
109
110
            } elseif ($this->entity && strtolower($key) === snake_case(class_basename($this->entity))) {
111
                $replacements += $this->eloquentModelTokens($attributes, $key);
112
113
            // For related taxonomy: https://github.com/fomvasss/laravel-taxonomy
114
            // and you set preffix in your relation methods - "tx"
115
            } elseif ($this->entity && substr($key, 0, 2) === 'tx') {
116
                $replacements += $this->eloquentModelTokens($attributes, $key);
117
            }
118
119
            if ($this->clearEmptyTokens) {
120
                $replacements += array_fill_keys($attributes, '');
121
            }
122
        }
123
124
        $attributes = array_keys($replacements);
125
        $values = array_values($replacements);
126
127
        return str_replace($attributes, $values, $this->text);
128
    }
129
130
    /**
131
     * Taken from the best CMS - Drupal :)
132
     * https://api.drupal.org/api/drupal/includes%21token.inc/function/token_scan/7.x
133
     * preg_match_all('/\[([^\]:]*):([^\]]*)\]/', $tokenStr, $matches);
134
     *
135
     * @param $text
136
     * @return array
137
     */
138
    private function tokenScan(string $text): array
139
    {
140
141
        // Matches tokens with the following pattern: [$type:$name]
142
        // $type and $name may not contain  [ ] characters.
143
        // $type may not contain : or whitespace characters, but $name may.
144
        preg_match_all('/
145
            \\[             # [ - pattern start
146
            ([^\\s\\[\\]:]*)  # match $type not containing whitespace : [ or ]
147
            :              # : - separator
148
            ([^\\[\\]]*)     # match $name not containing [ or ]
149
            \\]             # ] - pattern end
150
            /x', $text, $matches);
151
        $types = $matches[1];
152
        $tokens = $matches[2];
153
154
        // Iterate through the matches, building an associative array containing
155
        // $tokens grouped by $types, pointing to the version of the token found in
156
        // the source text. For example, $results['node']['title'] = '[node:title]';
157
        $results = [];
158
        for ($i = 0; $i < count($tokens); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
159
            $results[$types[$i]][$tokens[$i]] = $matches[0][$i];
160
        }
161
162
        return $results;
163
    }
164
165
    /**
166
     * @param array $tokens
167
     * @param string $type
168
     * @return array
169
     */
170
    protected function eloquentModelTokens(array $tokens, string $type): array
171
    {
172
        $replacements = [];
173
174
        foreach ($tokens as $key => $original) {
175
            $function = explode(':', $key)[0];
176
            $strTokenMethod = camel_case('str_token_'.$function);
177
178
            // Exists token generate method (defined user)
179
            if (method_exists($this->entity, $strTokenMethod)) {
180
181
                $replacements[$original] = $this->entity->{$strTokenMethod}($this->entity, ...explode(':', $key));
182
183
            // Exists relation function (defined user)
184
            } elseif (method_exists($this->entity, $function)) {
185
186
                $newOriginal = str_replace("$type:", '', $original);
187
188
                if ($this->entity->{$function} instanceof Model) {
189
                    $tm = new static();
190
191
                    $replacements[$original] = $tm->setText($newOriginal)->setEntity($this->entity->{$function})->replace();
192
                } elseif ($this->entity->{$function} instanceof Collection && ($firstRelatedEntity = $this->entity->{$function}->first())) {
193
                    $tm = new static();
194
195
                    $replacements[$original] = $tm->setText($newOriginal)->setEntity($firstRelatedEntity)->replace();
196
                }
197
                // is field model
198
            } else {
199
                // TODO: check available fields
200
                $replacements[$original] = $this->entity->{$key};
201
            }
202
        }
203
204
        return $replacements;
205
    }
206
207
    /**
208
     * TODO: require https://github.com/fomvasss/laravel-variables
209
     *
210
     * @param $tokens
211
     * @return array
212
     */
213
    protected function variablesTokens(array $tokens): array
214
    {
215
        $replacements = [];
216
217
        foreach ($tokens as $name => $original) {
218
            $replacements[$original] = var_get($name);
0 ignored issues
show
Bug introduced by
The function var_get was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

218
            $replacements[$original] = /** @scrutinizer ignore-call */ var_get($name);
Loading history...
219
        }
220
221
        return $replacements;
222
    }
223
224
    /**
225
     * @param array $tokens
226
     * @return array
227
     */
228
    protected function configTokens(array $tokens): array
229
    {
230
        $replacements = [];
231
232
        foreach ($tokens as $name => $original) {
233
            $res = config($name, '');
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

233
            $res = /** @scrutinizer ignore-call */ config($name, '');
Loading history...
234
            $replacements[$original] = is_string($res) ? $res : '';
235
        }
236
237
        return $replacements;
238
    }
239
240
    /**
241
     * @param $tokens
242
     * @return array
243
     */
244
    protected function dateTokens(array $tokens):array
245
    {
246
        $this->date = $this->date ?: Carbon::now();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->date ?: Carbon\Carbon::now() can also be of type Carbon\Carbon. However, the property $date is declared as type null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
247
        $replacements = [];
248
249
        foreach ($tokens as $name => $original) {
250
            if ($name === 'raw') {
251
                $replacements[$original] = $this->date;
252
            } else {
253
                $format = config('str-tokens.date.formats.'.$name, 'D, m/d/Y - H:i');
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

253
                $format = /** @scrutinizer ignore-call */ config('str-tokens.date.formats.'.$name, 'D, m/d/Y - H:i');
Loading history...
254
                $replacements[$original] = $this->date->format($format);
255
            }
256
        }
257
258
        return $replacements;
259
    }
260
}