Passed
Push — master ( da6314...127f02 )
by Lee
04:23 queued 11s
created

Enforcer::addDomainMatchingFunc()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
1
<?php
2
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
declare(strict_types=1);
4
5
namespace Casbin;
6
7
use Casbin\Effect\DefaultEffector;
8
use Casbin\Effect\Effector;
9
use Casbin\Exceptions\CasbinException;
10
use Casbin\Exceptions\InvalidFilePathException;
11
use Casbin\Model\FunctionMap;
12
use Casbin\Model\Model;
13
use Casbin\Persist\Adapter;
14
use Casbin\Persist\Adapters\FileAdapter;
15
use Casbin\Persist\FilteredAdapter;
16
use Casbin\Persist\Watcher;
17
use Casbin\Rbac\DefaultRoleManager\RoleManager as DefaultRoleManager;
18
use Casbin\Rbac\RoleManager;
19
use Casbin\Util\BuiltinOperations;
20
use Casbin\Log\Log;
21
use Casbin\Util\Util;
22
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
23
24
/**
25
 * Class Enforcer
26
 * the main interface for authorization enforcement and policy management.
27
 *
28
 * @author [email protected]
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
29
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package 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...
30
class Enforcer
31
{
32
    use InternalApi;
33
    use ManagementApi;
34
    use RbacApi;
0 ignored issues
show
Bug introduced by
The trait Casbin\RbacApi requires the property $rM which is not provided by Casbin\Enforcer.
Loading history...
35
36
    /**
37
     * model path.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
38
     *
39
     * @var string
40
     */
41
    protected $modelPath;
42
43
    /**
44
     * Model.
45
     *
46
     * @var Model
47
     */
48
    protected $model;
49
50
    /**
51
     * FunctionMap.
52
     *
53
     * @var FunctionMap
54
     */
55
    protected $fm;
56
57
    /**
58
     * Effector.
59
     *
60
     * @var Effector
61
     */
62
    protected $eft;
63
64
    /**
65
     * Adapter.
66
     *
67
     * @var Adapter|null
68
     */
69
    protected $adapter;
70
71
    /**
72
     * Watcher.
73
     *
74
     * @var Watcher|null
75
     */
76
    protected $watcher;
77
78
    /**
79
     * RoleManager.
80
     *
81
     * @var RoleManager
82
     */
83
    protected $rm;
84
85
    /**
86
     * $enabled.
87
     *
88
     * @var bool
89
     */
90
    protected $enabled;
91
92
    /**
93
     * $autoSave.
94
     *
95
     * @var bool
96
     */
97
    protected $autoSave;
98
99
    /**
100
     * $autoBuildRoleLinks.
101
     *
102
     * @var bool
103
     */
104
    protected $autoBuildRoleLinks;
105
106
    /**
107
     * Enforcer constructor.
108
     * Creates an enforcer via file or DB.
109
     * File:
110
     * $e = new Enforcer("path/to/basic_model.conf", "path/to/basic_policy.csv")
111
     * MySQL DB:
112
     * $a = DatabaseAdapter::newAdapter([
113
     *      'type'     => 'mysql', // mysql,pgsql,sqlite,sqlsrv
114
     *      'hostname' => '127.0.0.1',
115
     *      'database' => 'test',
116
     *      'username' => 'root',
117
     *      'password' => '123456',
118
     *      'hostport' => '3306',
119
     *  ]);
120
     * $e = new Enforcer("path/to/basic_model.conf", $a).
121
     *
122
     * @param mixed ...$params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
123
     *
124
     * @throws CasbinException
125
     */
126 177
    public function __construct(...$params)
127
    {
128 177
        $parsedParamLen = 0;
129 177
        $paramLen = \count($params);
130 177
        if ($paramLen >= 1) {
131 177
            if (\is_bool($enableLog = $params[$paramLen - 1])) {
132 6
                $this->enableLog($enableLog);
133 6
                ++$parsedParamLen;
134
            }
135
        }
136
137 177
        if (2 == $paramLen - $parsedParamLen) {
138 156
            $p0 = $params[0];
139 156
            if (\is_string($p0)) {
140 150
                $p1 = $params[1];
141 150
                if (\is_string($p1)) {
142 147
                    $this->initWithFile($p0, $p1);
143
                } else {
144 150
                    $this->initWithAdapter($p0, $p1);
145
                }
146
            } else {
147 6
                if (\is_string($params[1])) {
148
                    throw new CasbinException('Invalid parameters for enforcer.');
149
                } else {
150 156
                    $this->initWithModelAndAdapter($p0, $params[1]);
151
                }
152
            }
153 24
        } elseif (1 == $paramLen - $parsedParamLen) {
154 21
            $p0 = $params[0];
155 21
            if (\is_string($p0)) {
156 12
                $this->initWithFile($p0, '');
157
            } else {
158 21
                $this->initWithModelAndAdapter($p0, null);
159
            }
160 3
        } elseif (0 == $paramLen - $parsedParamLen) {
161
            // pass
162
        } else {
163
            throw new CasbinException('Invalid parameters for enforcer.');
164
        }
165 177
    }
166
167
    /**
168
     * initializes an enforcer with a model file and a policy file.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
169
     *
170
     * @param string $modelPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
171
     * @param string $policyPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
172
     *
173
     * @throws CasbinException
174
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
175 159
    public function initWithFile(string $modelPath, string $policyPath): void
176
    {
177 159
        $adapter = new FileAdapter($policyPath);
178 159
        $this->initWithAdapter($modelPath, $adapter);
179 159
    }
180
181
    /**
182
     * initializes an enforcer with a database adapter.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
183
     *
184
     * @param string  $modelPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
185
     * @param Adapter $adapter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
186
     *
187
     * @throws CasbinException
188
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
189 162
    public function initWithAdapter(string $modelPath, Adapter $adapter): void
190
    {
191 162
        $m = Model::newModelFromFile($modelPath);
192 162
        $this->initWithModelAndAdapter($m, $adapter);
193
194 162
        $this->modelPath = $modelPath;
195 162
    }
196
197
    /**
198
     * initWithModelAndAdapter initializes an enforcer with a model and a database adapter.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
199
     *
200
     * @param Model        $m
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
201
     * @param Adapter|null $adapter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
202
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
203 174
    public function initWithModelAndAdapter(Model $m, Adapter $adapter = null): void
204
    {
205 174
        $this->adapter = $adapter;
206
207 174
        $this->model = $m;
208 174
        $this->model->printModel();
209
210 174
        $this->fm = Model::loadFunctionMap();
211
212 174
        $this->initialize();
213
214
        // Do not initialize the full policy when using a filtered adapter
215 174
        $ok = $this->adapter instanceof FilteredAdapter ? $this->adapter->isFiltered() : false;
216
217 174
        if (!\is_null($this->adapter) && !$ok) {
218 165
            $this->loadPolicy();
219
        }
220 174
    }
221
222
    /**
223
     * initializes an enforcer with a database adapter.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
224
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
225 177
    protected function initialize(): void
226
    {
227 177
        $this->rm = new DefaultRoleManager(10);
228 177
        $this->eft = new DefaultEffector();
229 177
        $this->watcher = null;
230
231 177
        $this->enabled = true;
232 177
        $this->autoSave = true;
233 177
        $this->autoBuildRoleLinks = true;
234 177
    }
235
236
    /**
237
     * reloads the model from the model CONF file.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
238
     * Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy().
239
     *
240
     * @throws CasbinException
241
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
242 3
    public function loadModel(): void
243
    {
244 3
        $this->model = Model::newModelFromFile($this->modelPath);
245 3
        $this->model->printModel();
246 3
        $this->fm = Model::loadFunctionMap();
247
248 3
        $this->initialize();
249 3
    }
250
251
    /**
252
     * gets the current model.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
253
     *
254
     * @return Model
255
     */
256 9
    public function getModel(): Model
257
    {
258 9
        return $this->model;
259
    }
260
261
    /**
262
     * sets the current model.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
263
     *
264
     * @param Model $model
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
265
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
266 6
    public function setModel(Model $model): void
267
    {
268 6
        $this->model = $model;
269 6
        $this->fm = $this->model->loadFunctionMap();
270
271 6
        $this->initialize();
272 6
    }
273
274
    /**
275
     * gets the current adapter.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
276
     *
277
     * @return Adapter
278
     */
279 6
    public function getAdapter(): Adapter
280
    {
281 6
        return $this->adapter;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->adapter could return the type null which is incompatible with the type-hinted return Casbin\Persist\Adapter. Consider adding an additional type-check to rule them out.
Loading history...
282
    }
283
284
    /**
285
     * sets the current adapter.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
286
     *
287
     * @param Adapter $adapter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
288
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
289 9
    public function setAdapter(Adapter $adapter): void
290
    {
291 9
        $this->adapter = $adapter;
292 9
    }
293
294
    /**
295
     * sets the current watcher.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
296
     *
297
     * @param Watcher $watcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
298
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
299
    public function setWatcher(Watcher $watcher): void
300
    {
301
        $this->watcher = $watcher;
302
        $this->watcher->setUpdateCallback(function () {
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...
303
            $this->loadPolicy();
304
        });
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...
305
    }
306
307
    /**
308
     * gets the current role manager.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
309
     *
310
     * @return RoleManager
311
     */
312 6
    public function getRoleManager(): RoleManager
313
    {
314 6
        return $this->rm;
315
    }
316
317
    /**
318
     * sets the current role manager.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
319
     *
320
     * @param RoleManager $rm
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
321
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
322
    public function setRoleManager(RoleManager $rm): void
323
    {
324
        $this->rm = $rm;
325
    }
326
327
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
328
     * @param string   $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
329
     * @param \Closure $fn
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
330
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
331 6
    public function addMatchingFunc(string $name, \Closure $fn): void
332
    {
333 6
        $this->rm->addMatchingFunc($name, $fn);
334 6
    }
335
336
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
337
     * @param string   $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
338
     * @param \Closure $fn
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
339
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
340 6
    public function addDomainMatchingFunc(string $name, \Closure $fn): void
341
    {
342 6
        $this->rm->addDomainMatchingFunc($name, $fn);
0 ignored issues
show
Bug introduced by
The method addDomainMatchingFunc() does not exist on Casbin\Rbac\RoleManager. Since it exists in all sub-types, consider adding an abstract or default implementation to Casbin\Rbac\RoleManager. ( Ignorable by Annotation )

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

342
        $this->rm->/** @scrutinizer ignore-call */ 
343
                   addDomainMatchingFunc($name, $fn);
Loading history...
343 6
    }
344
345
    /**
346
     * sets the current effector.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
347
     *
348
     * @param Effector $eft
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
349
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
350
    public function setEffector(Effector $eft): void
351
    {
352
        $this->eft = $eft;
353
    }
354
355
    /**
356
     * clears all policy.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
357
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
358 3
    public function clearPolicy(): void
359
    {
360 3
        $this->model->clearPolicy();
361 3
    }
362
363
    /**
364
     * reloads the policy from file/database.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
365
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
366 171
    public function loadPolicy(): void
367
    {
368 171
        $this->model->clearPolicy();
369
370
        try {
371 171
            $this->adapter->loadPolicy($this->model);
0 ignored issues
show
Bug introduced by
The method loadPolicy() does not exist on null. ( Ignorable by Annotation )

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

371
            $this->adapter->/** @scrutinizer ignore-call */ 
372
                            loadPolicy($this->model);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
372 12
        } catch (InvalidFilePathException $e) {
373
            //throw $e;
374
        }
375
376 171
        $this->model->printPolicy();
377 171
        if ($this->autoBuildRoleLinks) {
378 171
            $this->buildRoleLinks();
379
        }
380 171
    }
381
382
    /**
383
     * reloads a filtered policy from file/database.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
384
     *
385
     * @param mixed $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
386
     *
387
     * @throws CasbinException
388
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
389 3
    public function loadFilteredPolicy($filter): void
390
    {
391 3
        $this->model->clearPolicy();
392
393 3
        if ($this->adapter instanceof FilteredAdapter) {
394 3
            $filteredAdapter = $this->adapter;
395 3
            $filteredAdapter->loadFilteredPolicy($this->model, $filter);
396
        } else {
397
            throw new CasbinException('filtered policies are not supported by this adapter');
398
        }
399
400 3
        $this->model->printPolicy();
401 3
        if ($this->autoBuildRoleLinks) {
402 3
            $this->buildRoleLinks();
403
        }
404 3
    }
405
406
    /**
407
     * returns true if the loaded policy has been filtered.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
408
     *
409
     * @return bool
410
     */
411 6
    public function isFiltered(): bool
412
    {
413 6
        if (!$this->adapter instanceof FilteredAdapter) {
414 3
            return false;
415
        }
416
417 3
        $filteredAdapter = $this->adapter;
418
419 3
        return $filteredAdapter->isFiltered();
420
    }
421
422
    /**
423
     * saves the current policy (usually after changed with Casbin API) back to file/database.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
424
     *
425
     * @return mixed
426
     *
427
     * @throws CasbinException
428
     */
429 6
    public function savePolicy(): void
430
    {
431 6
        if ($this->isFiltered()) {
432 3
            throw new CasbinException('cannot save a filtered policy');
433
        }
434
435 3
        $this->adapter->savePolicy($this->model);
436
437 3
        if (null !== $this->watcher) {
438
            $this->watcher->update();
439
        }
440 3
    }
441
442
    /**
443
     * changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
444
     *
445
     * @param bool $enabled
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
446
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
447 3
    public function enableEnforce(bool $enabled = true): void
448
    {
449 3
        $this->enabled = $enabled;
450 3
    }
451
452
    /**
453
     * changes whether Casbin will log messages to the Logger.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
454
     *
455
     * @param bool $enabled
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
456
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
457 6
    public function enableLog(bool $enabled = true): void
458
    {
459 6
        Log::getLogger()->enableLog($enabled);
460 6
    }
461
462
    /**
463
     * controls whether to save a policy rule automatically to the adapter when it is added or removed.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
464
     *
465
     * @param bool $autoSave
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
466
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
467 3
    public function enableAutoSave(bool $autoSave = true): void
468
    {
469 3
        $this->autoSave = $autoSave;
470 3
    }
471
472
    /**
473
     * controls whether to rebuild the role inheritance relations when a role is added or deleted.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
474
     *
475
     * @param bool $autoBuildRoleLinks
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
476
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
477
    public function enableAutoBuildRoleLinks(bool $autoBuildRoleLinks = true): void
478
    {
479
        $this->autoBuildRoleLinks = $autoBuildRoleLinks;
480
    }
481
482
    /**
483
     * manually rebuild the role inheritance relations.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
484
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
485 171
    public function buildRoleLinks(): void
486
    {
487 171
        $this->rm->clear();
488 171
        $this->model->buildRoleLinks($this->rm);
489 171
    }
490
491
    /**
492
     * use a custom matcher to decides whether a "subject" can access a "object" with the operation "action",
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
493
     * input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
494
     *
495
     * @param string $matcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
496
     * @param mixed  ...$rvals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
497
     *
498
     * @return bool|mixed
499
     *
500
     * @throws CasbinException
501
     */
502 120
    protected function enforcing(string $matcher, ...$rvals): bool
503
    {
504 120
        if (!$this->enabled) {
505 3
            return true;
506
        }
507
508 120
        $functions = $this->fm->getFunctions();
509
510 120
        if (isset($this->model['g'])) {
511 45
            foreach ($this->model['g'] as $key => $ast) {
512 45
                $rm = $ast->rM;
513 45
                $functions[$key] = BuiltinOperations::generateGFunction($rm);
514
            }
515
        }
516
517 120
        if (!isset($this->model['m']['m'])) {
518
            throw new CasbinException('model is undefined');
519
        }
520
521 120
        $expString = '';
522 120
        if ('' === $matcher) {
523 120
            $expString = $this->getExpString($this->model['m']['m']->value);
524
        } else {
525
            $expString = Util::removeComments(Util::escapeAssertion($matcher));
526
        }
527
528 120
        $rTokens = array_values($this->model['r']['r']->tokens);
529 120
        $pTokens = array_values($this->model['p']['p']->tokens);
530
531 120
        $rParameters = array_combine($rTokens, $rvals);
532
533 120
        if (false === $rParameters) {
534
            throw new CasbinException('invalid request size');
535
        }
536
537 120
        $expressionLanguage = $this->getExpressionLanguage($functions);
538 120
        $expression = $expressionLanguage->parse($expString, array_merge($rTokens, $pTokens));
539
540 120
        $policyEffects = [];
541 120
        $matcherResults = [];
542
543 120
        $policyLen = \count($this->model['p']['p']->policy);
544
545 120
        if (0 != $policyLen) {
546 114
            foreach ($this->model['p']['p']->policy as $i => $pvals) {
547 114
                $parameters = array_combine($pTokens, $pvals);
548 114
                if (false === $parameters) {
549
                    throw new CasbinException('invalid policy size');
550
                }
551
552 114
                $parameters = array_merge($rParameters, $parameters);
553 114
                $result = $expressionLanguage->evaluate($expression, $parameters);
554
555 114
                if (\is_bool($result)) {
556 114
                    if (!$result) {
557 108
                        $policyEffects[$i] = Effector::INDETERMINATE;
558
559 114
                        continue;
560
                    }
561
                } elseif (\is_float($result)) {
562
                    if (0 == $result) {
563
                        $policyEffects[$i] = Effector::INDETERMINATE;
564
565
                        continue;
566
                    } else {
567
                        $matcherResults[$i] = $result;
568
                    }
569
                } else {
570
                    throw new CasbinException('matcher result should be bool, int or float');
571
                }
572 111
                if (isset($parameters['p_eft'])) {
573 12
                    $eft = $parameters['p_eft'];
574 12
                    if ('allow' == $eft) {
575 9
                        $policyEffects[$i] = Effector::ALLOW;
576 12
                    } elseif ('deny' == $eft) {
577 9
                        $policyEffects[$i] = Effector::DENY;
578
                    } else {
579 12
                        $policyEffects[$i] = Effector::INDETERMINATE;
580
                    }
581
                } else {
582 99
                    $policyEffects[$i] = Effector::ALLOW;
583
                }
584
585 111
                if (isset($this->model['e']['e']) && 'priority(p_eft) || deny' == $this->model['e']['e']->value) {
586 78
                    break;
587
                }
588
            }
589
        } else {
590 12
            $parameters = $rParameters;
591 12
            foreach ($this->model['p']['p']->tokens as $token) {
592 12
                $parameters[$token] = '';
593
            }
594
595 12
            $result = $expressionLanguage->evaluate($expression, $parameters);
596
597 12
            if ($result) {
598 3
                $policyEffects[0] = Effector::ALLOW;
599
            } else {
600 12
                $policyEffects[0] = Effector::INDETERMINATE;
601
            }
602
        }
603
604 120
        $result = $this->eft->mergeEffects($this->model['e']['e']->value, $policyEffects, $matcherResults);
605
606 120
        if (Log::getLogger()->isEnabled()) {
607 24
            $reqStr = 'Request: ';
608 24
            $reqStr .= implode(', ', array_values($rvals));
609
610 24
            $reqStr .= sprintf(' ---> %s', (string) $result);
611 24
            Log::logPrint($reqStr);
612
        }
613
614 120
        return $result;
615
    }
616
617
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
618
     * @param array $functions
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
619
     *
620
     * @return ExpressionLanguage
621
     */
622 120
    protected function getExpressionLanguage(array $functions): ExpressionLanguage
623
    {
624 120
        $expressionLanguage = new ExpressionLanguage();
625 120
        foreach ($functions as $key => $func) {
626
            $expressionLanguage->register($key, function (...$args) use ($key) {
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...
627
                return sprintf($key.'(%1$s)', implode(',', $args));
628
            }, function ($arguments, ...$args) use ($func) {
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 16 spaces, but found 12.
Loading history...
629 60
                return $func(...$args);
630 120
            });
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...
631
        }
632
633 120
        return $expressionLanguage;
634
    }
635
636
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
637
     * @param string $expString
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
638
     *
639
     * @return string
640
     */
641 120
    protected function getExpString(string $expString): string
642
    {
643 120
        return preg_replace_callback(
644 120
            '/([\s\S]*in\s+)\(([\s\S]+)\)([\s\S]*)/',
645
            function ($m) {
646
                return $m[1].'['.$m[2].']'.$m[3];
647 120
            },
648 80
            $expString
649
        );
650
    }
651
652
    /**
653
     * decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
654
     *
655
     * @param mixed ...$rvals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
656
     *
657
     * @return bool
658
     *
659
     * @throws CasbinException
660
     */
661 120
    public function enforce(...$rvals): bool
662
    {
663 120
        return $this->enforcing('', ...$rvals);
664
    }
665
666
    /**
667
     * use a custom matcher to decides whether a "subject" can access a "object" with the operation "action",
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
668
     * input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
669
     *
670
     * @param string $matcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
671
     * @param mixed  ...$rvals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
672
     *
673
     * @return bool
674
     *
675
     * @throws CasbinException
676
     */
677
    public function enforceWithMatcher(string $matcher, ...$rvals): bool
678
    {
679
        return $this->enforcing($matcher, ...$rvals);
680
    }
681
}
682