CoreEnforcer::enforcing()   F
last analyzed

Complexity

Conditions 34
Paths 10035

Size

Total Lines 181
Code Lines 116

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 96
CRAP Score 35.2558

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 34
eloc 116
c 3
b 1
f 0
nc 10035
nop 3
dl 0
loc 181
ccs 96
cts 107
cp 0.8972
crap 35.2558
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Effector\DefaultEffector;
8
use Casbin\Effector\Effector;
9
use Casbin\Exceptions\CasbinException;
10
use Casbin\Exceptions\EvalFunctionException;
11
use Casbin\Exceptions\InvalidFilePathException;
12
use Casbin\Log\Log;
13
use Casbin\Model\FunctionMap;
14
use Casbin\Model\Model;
15
use Casbin\Persist\Adapter;
16
use Casbin\Persist\Adapters\FileAdapter;
17
use Casbin\Persist\FilteredAdapter;
18
use Casbin\Persist\Watcher;
19
use Casbin\Persist\WatcherEx;
20
use Casbin\Rbac\DefaultRoleManager\RoleManager as DefaultRoleManager;
21
use Casbin\Rbac\RoleManager;
22
use Casbin\Util\BuiltinOperations;
23
use Casbin\Util\Util;
24
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
25
use Symfony\Component\ExpressionLanguage\ParsedExpression;
26
27
/**
28
 * Class CoreEnforcer
29
 * The main interface for authorization enforcement and policy management.
30
 *
31
 * @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...
32
 */
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...
33
class CoreEnforcer
34
{
35
    /**
36
     * Model path.
37
     *
38
     * @var string
39
     */
40
    protected $modelPath;
41
42
    /**
43
     * Model.
44
     *
45
     * @var Model
46
     */
47
    protected $model;
48
49
    /**
50
     * FunctionMap.
51
     *
52
     * @var FunctionMap
53
     */
54
    protected $fm;
55
56
    /**
57
     * Effector.
58
     *
59
     * @var Effector
60
     */
61
    protected $eft;
62
63
    /**
64
     * Adapter.
65
     *
66
     * @var Adapter|null
67
     */
68
    protected $adapter;
69
70
    /**
71
     * Watcher.
72
     *
73
     * @var Watcher|null
74
     */
75
    protected $watcher;
76
77
    /**
78
     * RmMap.
79
     *
80
     * @var RoleManager[]
81
     */
82
    protected $rmMap;
83
84
    /**
85
     * $enabled.
86
     *
87
     * @var bool
88
     */
89
    protected $enabled;
90
91
    /**
92
     * $autoSave.
93
     *
94
     * @var bool
95
     */
96
    protected $autoSave;
97
98
    /**
99
     * $autoBuildRoleLinks.
100
     *
101
     * @var bool
102
     */
103
    protected $autoBuildRoleLinks;
104
105
    /**
106
     * $autoNotifyWatcher.
107
     *
108
     * @var bool
109
     */
110
    protected $autoNotifyWatcher;
111
112
    /**
113
     * Enforcer constructor.
114
     * Creates an enforcer via file or DB.
115
     * File:
116
     * $e = new Enforcer("path/to/basic_model.conf", "path/to/basic_policy.csv")
117
     * MySQL DB:
118
     * $a = DatabaseAdapter::newAdapter([
119
     *      'type'     => 'mysql', // mysql,pgsql,sqlite,sqlsrv
120
     *      'hostname' => '127.0.0.1',
121
     *      'database' => 'test',
122
     *      'username' => 'root',
123
     *      'password' => '123456',
124
     *      'hostport' => '3306',
125
     *  ]);
126
     * $e = new Enforcer("path/to/basic_model.conf", $a).
127
     *
128
     * @param mixed ...$params
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
129
     *
130
     * @throws CasbinException
131
     */
132 336
    public function __construct(...$params)
133
    {
134 336
        $parsedParamLen = 0;
135 336
        $paramLen = \count($params);
136 336
        if ($paramLen >= 1) {
137 333
            if (\is_bool($enableLog = $params[$paramLen - 1])) {
138 6
                $this->enableLog($enableLog);
139 6
                ++$parsedParamLen;
140
            }
141
        }
142
143 336
        if (2 == $paramLen - $parsedParamLen) {
144 303
            $p0 = $params[0];
145 303
            if (\is_string($p0)) {
146 297
                $p1 = $params[1];
147 297
                if (\is_string($p1)) {
148 288
                    $this->initWithFile($p0, $p1);
149
                } else {
150 297
                    $this->initWithAdapter($p0, $p1);
151
                }
152
            } else {
153 6
                if (\is_string($params[1])) {
154
                    throw new CasbinException('Invalid parameters for enforcer.');
155
                } else {
156 303
                    $this->initWithModelAndAdapter($p0, $params[1]);
157
                }
158
            }
159 36
        } elseif (1 == $paramLen - $parsedParamLen) {
160 30
            $p0 = $params[0];
161 30
            if (\is_string($p0)) {
162 21
                $this->initWithFile($p0, '');
163
            } else {
164 30
                $this->initWithModelAndAdapter($p0, null);
165
            }
166 6
        } elseif (0 == $paramLen - $parsedParamLen) {
167
            // pass
168
        } else {
169
            throw new CasbinException('Invalid parameters for enforcer.');
170
        }
171 224
    }
172
173
    /**
174
     * Initializes an enforcer with a model file and a policy file.
175
     *
176
     * @param string $modelPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
177
     * @param string $policyPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
178
     *
179
     * @throws CasbinException
180
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
181 309
    public function initWithFile(string $modelPath, string $policyPath): void
182
    {
183 309
        $adapter = new FileAdapter($policyPath);
184 309
        $this->initWithAdapter($modelPath, $adapter);
185 206
    }
186
187
    /**
188
     * Initializes an enforcer with a database adapter.
189
     *
190
     * @param string $modelPath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
191
     * @param Adapter $adapter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
192
     *
193
     * @throws CasbinException
194
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
195 321
    public function initWithAdapter(string $modelPath, Adapter $adapter): void
196
    {
197 321
        $m = Model::newModelFromFile($modelPath);
198 321
        $this->initWithModelAndAdapter($m, $adapter);
199
200 321
        $this->modelPath = $modelPath;
201 214
    }
202
203
    /**
204
     * InitWithModelAndAdapter initializes an enforcer with a model and a database adapter.
205
     *
206
     * @param Model $m
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
207
     * @param Adapter|null $adapter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
208
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
209 333
    public function initWithModelAndAdapter(Model $m, Adapter $adapter = null): void
210
    {
211 333
        $this->adapter = $adapter;
212 333
        $this->model = $m;
213 333
        $this->model->printModel();
214
215 333
        $this->fm = Model::loadFunctionMap();
216
217 333
        $this->initialize();
218
219
        // Do not initialize the full policy when using a filtered adapter
220 333
        $ok = $this->adapter instanceof FilteredAdapter ? $this->adapter->isFiltered() : false;
221
222 333
        if (!\is_null($this->adapter) && !$ok) {
223 321
            $this->loadPolicy();
224
        }
225 222
    }
226
227
    /**
228
     * Initializes an enforcer with a database adapter.
229
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
230 336
    protected function initialize(): void
231
    {
232 336
        $this->rmMap = [];
233 336
        $this->eft = new DefaultEffector();
234 336
        $this->watcher = null;
235
236 336
        $this->enabled = true;
237 336
        $this->autoSave = true;
238 336
        $this->autoBuildRoleLinks = true;
239 336
        $this->autoNotifyWatcher = true;
240 336
        $this->initRmMap();
241 224
    }
242
243
    /**
244
     * Reloads the model from the model CONF file.
245
     * Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy().
246
     *
247
     * @throws CasbinException
248
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
249 3
    public function loadModel(): void
250
    {
251 3
        $this->model = Model::newModelFromFile($this->modelPath);
252 3
        $this->model->printModel();
253 3
        $this->fm = Model::loadFunctionMap();
254
255 3
        $this->initialize();
256 2
    }
257
258
    /**
259
     * Gets the current model.
260
     *
261
     * @return Model
262
     */
263 12
    public function getModel(): Model
264
    {
265 12
        return $this->model;
266
    }
267
268
    /**
269
     * Sets the current model.
270
     *
271
     * @param Model $model
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
272
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
273 6
    public function setModel(Model $model): void
274
    {
275 6
        $this->model = $model;
276 6
        $this->fm = $this->model->loadFunctionMap();
277
278 6
        $this->initialize();
279 4
    }
280
281
    /**
282
     * Gets the current adapter.
283
     *
284
     * @return Adapter|null
285
     */
286 6
    public function getAdapter(): ?Adapter
287
    {
288 6
        return $this->adapter;
289
    }
290
291
    /**
292
     * Sets the current adapter.
293
     *
294
     * @param Adapter $adapter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
295
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
296 12
    public function setAdapter(Adapter $adapter): void
297
    {
298 12
        $this->adapter = $adapter;
299 8
    }
300
301
    /**
302
     * Sets the current watcher.
303
     *
304
     * @param Watcher $watcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
305
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
306 27
    public function setWatcher(Watcher $watcher): void
307
    {
308 27
        $this->watcher = $watcher;
309 9
        $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...
310
            $this->loadPolicy();
311 27
        });
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...
312 18
    }
313
314
    /**
315
     * Gets the current role manager.
316
     *
317
     * @return RoleManager
318
     */
319 6
    public function getRoleManager(): RoleManager
320
    {
321 6
        return $this->rmMap['g'];
322
    }
323
324
    /**
325
     * Gets the current role manager.
326
     *
327
     * @param RoleManager $rm
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
328
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
329
    public function setRoleManager(RoleManager $rm): void
330
    {
331
        $this->rmMap['g'] = $rm;
332
    }
333
334
    /**
335
     * Sets the current effector.
336
     *
337
     * @param Effector $eft
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
338
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
339
    public function setEffector(Effector $eft): void
340
    {
341
        $this->eft = $eft;
342
    }
343
344
    /**
345
     * Clears all policy.
346
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
347 6
    public function clearPolicy(): void
348
    {
349 6
        $this->model->clearPolicy();
350 4
    }
351
352
    /**
353
     * Reloads the policy from file/database.
354
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
355 330
    public function loadPolicy(): void
356
    {
357 330
        $flag = false;
358 330
        $needToRebuild = false;
359 330
        $newModel = clone $this->model;
360 330
        $newModel->clearPolicy();
361
362
        try {
363 330
            if (!is_null($this->adapter)){
0 ignored issues
show
Coding Style introduced by
Expected "if (...) {\n"; found "if (...){\n"
Loading history...
Coding Style introduced by
There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement; found 0 spaces
Loading history...
364 330
                $this->adapter->loadPolicy($newModel);
365
            }
366 309
            $newModel->printPolicy();
367 309
            $newModel->sortPoliciesBySubjectHierarchy();
368 309
            $newModel->sortPoliciesByPriority();
369
370 309
            if ($this->autoBuildRoleLinks) {
371 309
                $needToRebuild = true;
372 309
                foreach ($this->rmMap as $rm) {
373 204
                    $rm->clear();
374
                }
375 309
                $newModel->buildRoleLinks($this->rmMap);
376
            }
377 309
            $this->model = $newModel;
378 27
        } catch (InvalidFilePathException $e) {
379
            // Ignore throw $e;
380
        } catch (\Throwable $e) {
381
            $flag = true;
382
            throw $e;
383 220
        } finally {
384 330
            if ($flag) {
385
                if ($this->autoBuildRoleLinks && $needToRebuild) {
386 330
                    $this->buildRoleLinks();
387
                }
388
            }
389
        }
390 220
    }
391
392
    /**
393
     * Reloads a filtered policy from file/database.
394
     *
395
     * @param mixed $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
396
     *
397
     * @throws CasbinException
398
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
399 6
    public function _loadFilteredPolicy($filter): void
0 ignored issues
show
Coding Style introduced by
Public method name "CoreEnforcer::_loadFilteredPolicy" must not be prefixed with an underscore
Loading history...
400
    {
401 6
        if ($this->adapter instanceof FilteredAdapter) {
402 6
            $filteredAdapter = $this->adapter;
403 6
            $filteredAdapter->loadFilteredPolicy($this->model, $filter);
404
        } else {
405
            throw new CasbinException('filtered policies are not supported by this adapter');
406
        }
407
408 6
        $this->model->sortPoliciesByPriority();
409 6
        $this->initRmMap();
410 6
        $this->model->printPolicy();
411 6
        if ($this->autoBuildRoleLinks) {
412 6
            $this->buildRoleLinks();
413
        }
414 4
    }
415
416
    /**
417
     * Reloads a filtered policy from file/database.
418
     *
419
     * @param mixed $filter
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
420
     *
421
     * @throws CasbinException
422
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
423 6
    public function loadFilteredPolicy($filter): void
424
    {
425 6
        $this->model->clearPolicy();
426
427 6
        $this->_loadFilteredPolicy($filter);
428 4
    }
429
430
    /**
431
     * LoadIncrementalFilteredPolicy append a filtered policy from file/database.
432
     *
433
     * @param mixed $filter
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...
434
     * @return void
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
435
     */
436 3
    public function loadIncrementalFilteredPolicy($filter): void
437
    {
438 3
        $this->_loadFilteredPolicy($filter);
439 2
    }
440
441
    /**
442
     * Returns true if the loaded policy has been filtered.
443
     *
444
     * @return bool
445
     */
446 6
    public function isFiltered(): bool
447
    {
448 6
        if (!$this->adapter instanceof FilteredAdapter) {
449 3
            return false;
450
        }
451
452 3
        $filteredAdapter = $this->adapter;
453
454 3
        return $filteredAdapter->isFiltered();
455
    }
456
457
    /**
458
     * Saves the current policy (usually after changed with Casbin API) back to file/database.
459
     *
460
     * @throws CasbinException
461
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
462 6
    public function savePolicy(): void
463
    {
464 6
        if ($this->isFiltered()) {
465 3
            throw new CasbinException('cannot save a filtered policy');
466
        }
467
468 3
        if (!is_null($this->adapter)){
0 ignored issues
show
Coding Style introduced by
Expected "if (...) {\n"; found "if (...){\n"
Loading history...
Coding Style introduced by
There must be a single space between the closing parenthesis and the opening brace of a multi-line IF statement; found 0 spaces
Loading history...
469 3
            $this->adapter->savePolicy($this->model);
470
        }        
471
472 3
        if ($this->watcher !== null && $this->autoNotifyWatcher) {
473
            if ($this->watcher instanceof WatcherEx) {
474
                $this->watcher->updateForSavePolicy($this->model);
475
            } else {
476
                $this->watcher->update();
477
            }
478
        }
479 2
    }
480
481
    /**
482
     * initRmMap initializes rmMap.
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
483
     *
484
     * @return void
485
     */
486 336
    public function initRmMap(): void
487
    {
488 336
        if (isset($this->model['g'])) {
489 213
            foreach ($this->model['g'] as $ptype => $value) {
490 213
                if (isset($this->rmMap[$ptype])) {
491 6
                    $rm = $this->rmMap[$ptype];
492 6
                    $rm->clear();
493
                } else {
494 213
                    $this->rmMap[$ptype] = new DefaultRoleManager(10);
495
                }
496
            }
497
        }
498 224
    }
499
500
    /**
501
     * Changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function.
502
     *
503
     * @param bool $enabled
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
504
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
505 3
    public function enableEnforce(bool $enabled = true): void
506
    {
507 3
        $this->enabled = $enabled;
508 2
    }
509
510
    /**
511
     * Changes whether Casbin will log messages to the Logger.
512
     *
513
     * @param bool $enabled
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
514
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
515 6
    public function enableLog(bool $enabled = true): void
516
    {
517 6
        Log::getLogger()->enableLog($enabled);
518 4
    }
519
520
    /**
521
     * Controls whether to save a policy rule automatically notify the Watcher when it is added or removed.
522
     *
523
     * @param bool $enabled
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
524
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
525
    public function enableAutoNotifyWatcher(bool $enabled = true): void
526
    {
527
        $this->autoNotifyWatcher = $enabled;
528
    }
529
530
    /**
531
     * Controls whether to save a policy rule automatically to the adapter when it is added or removed.
532
     *
533
     * @param bool $autoSave
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
534
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
535 3
    public function enableAutoSave(bool $autoSave = true): void
536
    {
537 3
        $this->autoSave = $autoSave;
538 2
    }
539
540
    /**
541
     * Controls whether to rebuild the role inheritance relations when a role is added or deleted.
542
     *
543
     * @param bool $autoBuildRoleLinks
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
544
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
545
    public function enableAutoBuildRoleLinks(bool $autoBuildRoleLinks = true): void
546
    {
547
        $this->autoBuildRoleLinks = $autoBuildRoleLinks;
548
    }
549
550
    /**
551
     * Manually rebuild the role inheritance relations.
552
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
553 42
    public function buildRoleLinks(): void
554
    {
555 42
        foreach ($this->rmMap as $rm) {
556 42
            $rm->clear();
557
        }
558
559 42
        $this->model->buildRoleLinks($this->rmMap);
560 28
    }
561
562
    /**
563
     * Use a custom matcher to decides whether a "subject" can access a "object" with the operation "action",
564
     * input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
565
     *
566
     * @param string $matcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
567
     * @param array $explains
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
568
     * @param mixed ...$rvals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
569
     *
570
     * @return bool
571
     *
572
     * @throws CasbinException
573
     */
574 210
    protected function enforcing(string $matcher, &$explains = [], ...$rvals): bool
575
    {
576 210
        if (!$this->enabled) {
577 3
            return true;
578
        }
579
580 210
        $functions = $this->fm->getFunctions();
581
582 210
        if (isset($this->model['g'])) {
583 93
            foreach ($this->model['g'] as $key => $ast) {
584 93
                $rm = $ast->rm;
585 93
                $functions[$key] = BuiltinOperations::generateGFunction($rm);
586
            }
587
        }
588
589 210
        if (!isset($this->model['m']['m'])) {
590
            throw new CasbinException('model is undefined');
591
        }
592
593 210
        $rType = "r";
594 210
        $pType = "p";
595 210
        $eType = "e";
596 210
        $mType = "m";
597
598
        switch (true) {
599 210
            case $rvals[0] instanceof EnforceContext:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
600 3
                $enforceContext = $rvals[0];
601 3
                $rType = $enforceContext->rType;
602 3
                $pType = $enforceContext->pType;
603 3
                $eType = $enforceContext->eType;
604 3
                $mType = $enforceContext->mType;
605 3
                array_shift($rvals);
606 3
                break;
607
            default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
608 210
                break;
609
        }
610
611 210
        $expString = '';
612 210
        if ('' === $matcher) {
613 210
            $expString = $this->model['m'][$mType]->value;
614
        } else {
615
            $expString = Util::removeComments(Util::escapeAssertion($matcher));
616
        }
617
618 210
        $rTokens = array_values($this->model['r'][$rType]->tokens);
619 210
        $pTokens = array_values($this->model['p'][$pType]->tokens);
620
621 210
        if (\count($rTokens) != \count($rvals)) {
622
            throw new CasbinException(\sprintf('invalid request size: expected %d, got %d', \count($rTokens), \count($rvals)));
623
        }
624 210
        $rParameters = array_combine($rTokens, $rvals);
625
626 210
        if (false == $rParameters) {
627
            throw new CasbinException('invalid request size');
628
        }
629
630 210
        $expressionLanguage = $this->getExpressionLanguage($functions);
631 210
        $expression = "";
632
633 210
        $hasEval = Util::hasEval($expString);
634
635 210
        if (!$hasEval) {
636 204
            $expression = $expressionLanguage->parse($expString, array_merge($rTokens, $pTokens));
637
        }
638
639 210
        $policyEffects = [];
640 210
        $matcherResults = [];
641
642 210
        $effect = 0;
643 210
        $explainIndex = 0;
644
645 210
        $policyLen = \count($this->model['p'][$pType]->policy);
646 210
        if (0 != $policyLen && (strpos($expString, $pType . '_') !== false)) {
647 192
            foreach ($this->model['p'][$pType]->policy as $policyIndex => $pvals) {
648 192
                $parameters = array_combine($pTokens, $pvals);
649 192
                if (false == $parameters) {
650
                    throw new CasbinException('invalid policy size');
651
                }
652
653 192
                if ($hasEval) {
654 6
                    $ruleNames = Util::getEvalValue($expString);
655 6
                    $replacements = [];
656 6
                    $pTokens_flipped = array_flip($pTokens);
657 6
                    foreach ($ruleNames as $ruleName) {
658 6
                        if (isset($pTokens_flipped[$ruleName])) {
659 6
                            $rule = Util::escapeAssertion($pvals[$pTokens_flipped[$ruleName]]);
660 6
                            $replacements[$ruleName] = $rule;
661
                        } else {
662 4
                            throw new CasbinException('please make sure rule exists in policy when using eval() in matcher');
663
                        }
664
                    }
665
666 6
                    $expWithRule = Util::replaceEvalWithMap($expString, $replacements);
667 6
                    $expression = $expressionLanguage->parse($expWithRule, array_merge($rTokens, $pTokens));
668
                }
669
670 192
                $parameters = array_merge($rParameters, $parameters);
671 192
                $result = $expressionLanguage->evaluate($expression, $parameters);
672
673
                // set to no-match at first
674 192
                $matcherResults[$policyIndex] = 0;
675 192
                if (\is_bool($result)) {
676 192
                    if ($result) {
677 192
                        $matcherResults[$policyIndex] = 1;
678
                    }
679
                } elseif (\is_float($result)) {
680
                    if ($result != 0) {
681
                        $matcherResults[$policyIndex] = 1;
682
                    }
683
                } else {
684
                    throw new CasbinException('matcher result should be bool, int or float');
685
                }
686 192
                if (isset($parameters[$pType . '_eft'])) {
687 36
                    $eft = $parameters[$pType . '_eft'];
688 36
                    if ('allow' == $eft) {
689 30
                        $policyEffects[$policyIndex] = Effector::ALLOW;
690 30
                    } elseif ('deny' == $eft) {
691 24
                        $policyEffects[$policyIndex] = Effector::DENY;
692
                    } else {
693 36
                        $policyEffects[$policyIndex] = Effector::INDETERMINATE;
694
                    }
695
                } else {
696 159
                    $policyEffects[$policyIndex] = Effector::ALLOW;
697
                }
698
699 192
                list($effect, $explainIndex) = $this->eft->mergeEffects($this->model['e'][$eType]->value, $policyEffects, $matcherResults, $policyIndex, $policyLen);
700 192
                if ($effect != Effector::INDETERMINATE) {
701 188
                    break;
702
                }
703
            }
704
        } else {
705 24
            if ($hasEval) {
706 3
                throw new EvalFunctionException("please make sure rule exists in policy when using eval() in matcher");
707
            }
708
709 21
            $matcherResults[0] = 1;
710
711 21
            $parameters = $rParameters;
712 21
            foreach ($this->model['p'][$pType]->tokens as $token) {
713 21
                $parameters[$token] = '';
714
            }
715
716 21
            $result = $expressionLanguage->evaluate($expression, $parameters);
717
718 21
            if ($result) {
719 9
                $policyEffects[0] = Effector::ALLOW;
720
            } else {
721 21
                $policyEffects[0] = Effector::INDETERMINATE;
722
            }
723
724 21
            list($effect, $explainIndex) = $this->eft->mergeEffects($this->model['e'][$eType]->value, $policyEffects, $matcherResults, 0, 1);
725
        }
726
727 207
        if ($explains !== null) {
0 ignored issues
show
introduced by
The condition $explains !== null is always true.
Loading history...
728 207
            if (($explainIndex != -1) && (count($this->model['p'][$pType]->policy) > $explainIndex)) {
729 180
                $explains = $this->model['p'][$pType]->policy[$explainIndex];
730
            }
731
        }
732
733 207
        $result = $effect == Effector::ALLOW;
734
735 207
        if (Log::getLogger()->isEnabled()) {
736 66
            $reqStr = 'Request: ';
737 66
            $reqStr .= implode(', ', array_values($rvals));
738
739 66
            $reqStr .= sprintf(" ---> %s\n", var_export($result, true));
740
741 66
            $reqStr = 'Hit Policy: ';
742 66
            if (count($explains) == count($explains, COUNT_RECURSIVE)) {
743
                // if $explains is not multidimensional
744 66
                $reqStr .= sprintf("%s \n", '[' . implode(', ', $explains) . ']');
745
            } else {
746
                // if $explains is multidimensional
747
                foreach ($explains as $i => $pval) {
748
                    $reqStr .= sprintf("%s \n", '[' . implode(', ', $pval) . ']');
749
                }
750
            }
751 66
            Log::logPrint($reqStr);
752
        }
753
754 207
        return $result;
755
    }
756
757
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
758
     * @param array $functions
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
759
     *
760
     * @return ExpressionLanguage
761
     */
762 210
    protected function getExpressionLanguage(array $functions): ExpressionLanguage
763
    {
764 210
        $expressionLanguage = new ExpressionLanguage();
765 210
        foreach ($functions as $key => $func) {
766 70
            $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...
767
                return sprintf($key . '(%1$s)', implode(',', $args));
768 70
            }, 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...
769 117
                return $func(...$args);
770 210
            });
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...
771
        }
772
773 210
        return $expressionLanguage;
774
    }
775
776
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
777
     * @param string $expString
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
778
     *
779
     * @return string
780
     */
781
    protected function getExpString(string $expString): string
782
    {
783
        return preg_replace_callback(
784
            '/([\s\S]*in\s+)\(([\s\S]+)\)([\s\S]*)/',
785
            function ($m) {
786
                return $m[1] . '[' . $m[2] . ']' . $m[3];
787
            },
788
            $expString
789
        );
790
    }
791
792
    /**
793
     * Decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
794
     *
795
     * @param mixed ...$rvals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
796
     *
797
     * @return bool
798
     *
799
     * @throws CasbinException
800
     */
801 162
    public function enforce(...$rvals): bool
802
    {
803 162
        $explains = [];
804 162
        return $this->enforcing('', $explains, ...$rvals);
805
    }
806
807
    /**
808
     * Use a custom matcher to decides whether a "subject" can access a "object" with the operation "action",
809
     * input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
810
     *
811
     * @param string $matcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
812
     * @param mixed ...$rvals
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
813
     *
814
     * @return bool
815
     *
816
     * @throws CasbinException
817
     */
818
    public function enforceWithMatcher(string $matcher, ...$rvals): bool
819
    {
820
        $explains = [];
821
        return $this->enforcing($matcher, $explains, ...$rvals);
822
    }
823
824
    /**
825
     * EnforceEx explain enforcement by informing matched rules
826
     *
827
     * @param mixed ...$rvals
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...
828
     * @return array
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
829
     */
830 48
    public function enforceEx(...$rvals)
831
    {
832 48
        $explain = [];
833 48
        $result = $this->enforcing("", $explain, ...$rvals);
834 48
        return [$result, $explain];
835
    }
836
837
    /**
838
     * BuildIncrementalRoleLinks provides incremental build the role inheritance relations.
839
     *
840
     * @param integer $op policy operations.
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
841
     * @param string $ptype policy type.
0 ignored issues
show
Coding Style introduced by
Expected 5 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...
842
     * @param string[][] $rules the rules.
0 ignored issues
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
843
     * @return void
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
844
     */
845 36
    public function buildIncrementalRoleLinks(int $op, string $ptype, array $rules): void
846
    {
847 36
        $this->model->buildIncrementalRoleLinks($this->rmMap, $op, "g", $ptype, $rules);
848 24
    }
849
850
    /**
851
     * BatchEnforce enforce in batches
852
     *
853
     * @param string[][] $requests
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...
854
     * @return bool[]
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
855
     */
856 3
    public function batchEnforce(array $requests): array
857
    {
858 1
        return array_map(function (array $request) {
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...
859 3
            return  $this->enforce(...$request);
860 3
        }, $requests);
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...
861
    }
862
863
    /**
864
     * BatchEnforceWithMatcher enforce with matcher in batches
865
     *
866
     * @param string $matcher
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 5 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...
867
     * @param string[][] $requests
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...
868
     * @return bool[]
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
869
     */
870
    public function batchEnforceWithMatcher(string $matcher, array $requests): array
871
    {
872
        return array_map(function (array $request) use ($matcher) {
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...
873
            return  $this->enforceWithMatcher($matcher, ...$request);
874
        }, $requests);
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...
875
    }
876
877
    /**
878
     * AddNamedMatchingFunc add MatchingFunc by ptype RoleManager
879
     *
880
     * @param string $ptype
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 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...
881
     * @param string $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 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...
882
     * @param \Closure $fn
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...
883
     * @return boolean
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
884
     */
885 12
    public function addNamedMatchingFunc(string $ptype, string $name, \Closure $fn): bool
886
    {
887 12
        if (isset($this->rmMap[$ptype])) {
888 12
            $rm = $this->rmMap[$ptype];
889 12
            $rm->addMatchingFunc($name, $fn);
890 12
            return true;
891
        }
892
        return false;
893
    }
894
895
    /**
896
     * AddNamedDomainMatchingFunc add MatchingFunc by ptype to RoleManager
897
     *
898
     * @param string $ptype
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 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...
899
     * @param string $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 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...
900
     * @param \Closure $fn
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...
901
     * @return boolean
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
902
     */
903 9
    public function addNamedDomainMatchingFunc(string $ptype, string $name, \Closure $fn): bool
904
    {
905 9
        if (isset($this->rmMap[$ptype])) {
906 9
            $rm = $this->rmMap[$ptype];
907 9
            $rm->addDomainMatchingFunc($name, $fn);
908 9
            return true;
909
        }
910
        return false;
911
    }
912
}
913