CoreEnforcer::__construct()   B
last analyzed

Complexity

Conditions 10
Paths 24

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 10.0578

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 28
c 1
b 0
f 0
nc 24
nop 1
dl 0
loc 38
ccs 22
cts 24
cp 0.9167
crap 10.0578
rs 7.6666

How to fix   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