Completed
Push — travis_trusty ( a0f759...e78e49 )
by André
37:53 queued 08:54
created

Repository   F

Complexity

Total Complexity 103

Size/Duplication

Total Lines 1089
Duplicated Lines 4.04 %

Coupling/Cohesion

Components 1
Dependencies 34

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 103
c 2
b 0
f 1
lcom 1
cbo 34
dl 44
loc 1089
rs 0.579

34 Methods

Rating   Name   Duplication   Size   Complexity  
A getContentTypeService() 17 17 2
A getRelationProcessor() 0 10 2
A getContentTypeDomainMapper() 0 13 2
B __construct() 0 43 4
A getNameableFieldTypeRegistry() 0 10 2
A getNameSchemaService() 0 15 2
A getCurrentUser() 0 10 2
A getCurrentUserReference() 0 4 1
A setCurrentUser() 0 15 3
A sudo() 0 14 3
C hasAccess() 0 61 18
D canUser() 0 85 14
A getContentService() 18 18 2
A getContentLanguageService() 0 14 2
A getLocationService() 0 17 2
A getTrashService() 0 15 2
A getSectionService() 0 14 2
A getUserService() 0 14 2
A getURLAliasService() 0 14 2
A getURLWildcardService() 0 14 2
A getObjectStateService() 0 14 2
A getRoleService() 0 16 2
A getLimitationService() 0 10 2
A getRoleDomainMapper() 0 10 2
A getSearchService() 0 16 2
A getFieldTypeService() 0 10 2
A getFieldTypeRegistry() 0 10 2
A getDomainMapper() 0 16 2
A getPermissionsCriterionHandler() 0 6 2
A beginTransaction() 0 7 1
C commit() 0 29 7
A rollback() 0 11 2
A commitEvent() 0 9 2
A createDateTime() 9 9 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Repository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Repository, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Repository class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Repository;
12
13
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
14
use eZ\Publish\API\Repository\Values\ValueObject;
15
use eZ\Publish\API\Repository\Values\User\User;
16
use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference;
17
use eZ\Publish\API\Repository\Values\User\Limitation;
18
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType;
19
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
20
use eZ\Publish\Core\Repository\Values\User\UserReference;
21
use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler;
22
use eZ\Publish\SPI\Search\Handler as SearchHandler;
23
use eZ\Publish\SPI\Limitation\Type as LimitationType;
24
use Exception;
25
use RuntimeException;
26
27
/**
28
 * Repository class.
29
 */
30
class Repository implements RepositoryInterface
31
{
32
    /**
33
     * Repository Handler object.
34
     *
35
     * @var \eZ\Publish\SPI\Persistence\Handler
36
     */
37
    protected $persistenceHandler;
38
39
    /**
40
     * Instance of main Search Handler.
41
     *
42
     * @var \eZ\Publish\SPI\Search\Handler
43
     */
44
    protected $searchHandler;
45
46
    /**
47
     * Currently logged in user object if already loaded.
48
     *
49
     * @var \eZ\Publish\API\Repository\Values\User\User|null
50
     */
51
    protected $currentUser;
52
53
    /**
54
     * Currently logged in user reference for permission purposes.
55
     *
56
     * @var \eZ\Publish\API\Repository\Values\User\UserReference
57
     */
58
    protected $currentUserRef;
59
60
    /**
61
     * Counter for the current sudo nesting level {@see sudo()}.
62
     *
63
     * @var int
64
     */
65
    private $sudoNestingLevel = 0;
66
67
    /**
68
     * Instance of content service.
69
     *
70
     * @var \eZ\Publish\API\Repository\ContentService
71
     */
72
    protected $contentService;
73
74
    /**
75
     * Instance of section service.
76
     *
77
     * @var \eZ\Publish\API\Repository\SectionService
78
     */
79
    protected $sectionService;
80
81
    /**
82
     * Instance of role service.
83
     *
84
     * @var \eZ\Publish\API\Repository\RoleService
85
     */
86
    protected $roleService;
87
88
    /**
89
     * Instance of search service.
90
     *
91
     * @var \eZ\Publish\API\Repository\SearchService
92
     */
93
    protected $searchService;
94
95
    /**
96
     * Instance of user service.
97
     *
98
     * @var \eZ\Publish\API\Repository\UserService
99
     */
100
    protected $userService;
101
102
    /**
103
     * Instance of language service.
104
     *
105
     * @var \eZ\Publish\API\Repository\LanguageService
106
     */
107
    protected $languageService;
108
109
    /**
110
     * Instance of location service.
111
     *
112
     * @var \eZ\Publish\API\Repository\LocationService
113
     */
114
    protected $locationService;
115
116
    /**
117
     * Instance of Trash service.
118
     *
119
     * @var \eZ\Publish\API\Repository\TrashService
120
     */
121
    protected $trashService;
122
123
    /**
124
     * Instance of content type service.
125
     *
126
     * @var \eZ\Publish\API\Repository\ContentTypeService
127
     */
128
    protected $contentTypeService;
129
130
    /**
131
     * Instance of object state service.
132
     *
133
     * @var \eZ\Publish\API\Repository\ObjectStateService
134
     */
135
    protected $objectStateService;
136
137
    /**
138
     * Instance of field type service.
139
     *
140
     * @var \eZ\Publish\API\Repository\FieldTypeService
141
     */
142
    protected $fieldTypeService;
143
144
    /**
145
     * Instance of FieldTypeRegistry.
146
     *
147
     * @var \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry
148
     */
149
    private $fieldTypeRegistry;
150
151
    /**
152
     * Instance of NameableFieldTypeRegistry.
153
     *
154
     * @var \eZ\Publish\Core\Repository\Helper\NameableFieldTypeRegistry
155
     */
156
    private $nameableFieldTypeRegistry;
157
158
    /**
159
     * Instance of name schema resolver service.
160
     *
161
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
162
     */
163
    protected $nameSchemaService;
164
165
    /**
166
     * Instance of relation processor service.
167
     *
168
     * @var \eZ\Publish\Core\Repository\Helper\RelationProcessor
169
     */
170
    protected $relationProcessor;
171
172
    /**
173
     * Instance of URL alias service.
174
     *
175
     * @var \eZ\Publish\Core\Repository\URLAliasService
176
     */
177
    protected $urlAliasService;
178
179
    /**
180
     * Instance of URL wildcard service.
181
     *
182
     * @var \eZ\Publish\Core\Repository\URLWildcardService
183
     */
184
    protected $urlWildcardService;
185
186
    /**
187
     * Service settings, first level key is service name.
188
     *
189
     * @var array
190
     */
191
    protected $serviceSettings;
192
193
    /**
194
     * Instance of role service.
195
     *
196
     * @var \eZ\Publish\Core\Repository\Helper\LimitationService
197
     */
198
    protected $limitationService;
199
200
    /**
201
     * @var \eZ\Publish\Core\Repository\Helper\RoleDomainMapper
202
     */
203
    protected $roleDomainMapper;
204
205
    /**
206
     * Instance of domain mapper.
207
     *
208
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
209
     */
210
    protected $domainMapper;
211
212
    /**
213
     * Instance of content type domain mapper.
214
     *
215
     * @var \eZ\Publish\Core\Repository\Helper\ContentTypeDomainMapper
216
     */
217
    protected $contentTypeDomainMapper;
218
219
    /**
220
     * Instance of permissions criterion handler.
221
     *
222
     * @var \eZ\Publish\Core\Repository\PermissionsCriterionHandler
223
     */
224
    protected $permissionsCriterionHandler;
225
226
    /**
227
     * Array of arrays of commit events indexed by the transaction count.
228
     *
229
     * @var array
230
     */
231
    protected $commitEventsQueue = array();
232
233
    /**
234
     * @var int
235
     */
236
    protected $transactionDepth = 0;
237
238
    /**
239
     * @var int
240
     */
241
    private $transactionCount = 0;
242
243
    /**
244
     * Constructor.
245
     *
246
     * Construct repository object with provided storage engine
247
     *
248
     * @param \eZ\Publish\SPI\Persistence\Handler $persistenceHandler
249
     * @param \eZ\Publish\SPI\Search\Handler $searchHandler
250
     * @param array $serviceSettings
251
     * @param \eZ\Publish\API\Repository\Values\User\UserReference|null $user
252
     */
253
    public function __construct(
254
        PersistenceHandler $persistenceHandler,
255
        SearchHandler $searchHandler,
256
        array $serviceSettings = array(),
257
        APIUserReference $user = null
258
    ) {
259
        $this->persistenceHandler = $persistenceHandler;
260
        $this->searchHandler = $searchHandler;
261
        $this->serviceSettings = $serviceSettings + array(
262
            'content' => array(),
263
            'contentType' => array(),
264
            'location' => array(),
265
            'section' => array(),
266
            'role' => array(),
267
            'user' => array(
268
                'anonymousUserID' => 10,
269
            ),
270
            'language' => array(),
271
            'trash' => array(),
272
            'io' => array(),
273
            'objectState' => array(),
274
            'search' => array(),
275
            'fieldType' => array(),
276
            'nameableFieldTypes' => array(),
277
            'urlAlias' => array(),
278
            'urlWildcard' => array(),
279
            'nameSchema' => array(),
280
            'languages' => array(),
281
        );
282
283
        if (!empty($this->serviceSettings['languages'])) {
284
            $this->serviceSettings['language']['languages'] = $this->serviceSettings['languages'];
285
        }
286
287
        if ($user instanceof User) {
288
            $this->currentUser = $user;
289
            $this->currentUserRef = new UserReference($user->getUserId());
290
        } elseif ($user instanceof APIUserReference) {
291
            $this->currentUserRef = $user;
292
        } else {
293
            $this->currentUserRef = new UserReference($this->serviceSettings['user']['anonymousUserID']);
294
        }
295
    }
296
297
    /**
298
     * Get current user.
299
     *
300
     * Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}
301
     *
302
     * @return \eZ\Publish\API\Repository\Values\User\User
303
     */
304
    public function getCurrentUser()
305
    {
306
        if ($this->currentUser === null) {
307
            $this->currentUser = $this->getUserService()->loadUser(
308
                $this->currentUserRef->getUserId()
309
            );
310
        }
311
312
        return $this->currentUser;
313
    }
314
315
    /**
316
     * Get current user reference.
317
     *
318
     * @since 5.4.5
319
     * @return \eZ\Publish\API\Repository\Values\User\UserReference
320
     */
321
    public function getCurrentUserReference()
322
    {
323
        return $this->currentUserRef;
324
    }
325
326
    /**
327
     * Sets the current user to the given $user.
328
     *
329
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $user
330
     *
331
     * @throws InvalidArgumentValue If UserReference does not contain a id
332
     */
333
    public function setCurrentUser(APIUserReference $user)
334
    {
335
        $id = $user->getUserId();
336
        if (!$id) {
337
            throw new InvalidArgumentValue('$user->getUserId()', $id);
338
        }
339
340
        if ($user instanceof User) {
341
            $this->currentUser = $user;
342
            $this->currentUserRef = new UserReference($id);
343
        } else {
344
            $this->currentUser = null;
345
            $this->currentUserRef = $user;
346
        }
347
    }
348
349
    /**
350
     * Allows API execution to be performed with full access sand-boxed.
351
     *
352
     * The closure sandbox will do a catch all on exceptions and rethrow after
353
     * re-setting the sudo flag.
354
     *
355
     * Example use:
356
     *     $location = $repository->sudo(
357
     *         function ( Repository $repo ) use ( $locationId )
358
     *         {
359
     *             return $repo->getLocationService()->loadLocation( $locationId )
360
     *         }
361
     *     );
362
     *
363
     *
364
     * @param \Closure $callback
365
     * @param \eZ\Publish\API\Repository\Repository $outerRepository
366
     *
367
     * @throws \RuntimeException Thrown on recursive sudo() use.
368
     * @throws \Exception Re throws exceptions thrown inside $callback
369
     *
370
     * @return mixed
371
     */
372
    public function sudo(\Closure $callback, RepositoryInterface $outerRepository = null)
373
    {
374
        ++$this->sudoNestingLevel;
375
        try {
376
            $returnValue = $callback($outerRepository !== null ? $outerRepository : $this);
377
        } catch (Exception $e) {
378
            --$this->sudoNestingLevel;
379
            throw $e;
380
        }
381
382
        --$this->sudoNestingLevel;
383
384
        return $returnValue;
385
    }
386
387
    /**
388
     * Check if user has access to a given module / function.
389
     *
390
     * Low level function, use canUser instead if you have objects to check against.
391
     *
392
     * @param string $module
393
     * @param string $function
394
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $user
395
     *
396
     * @return bool|array Bool if user has full or no access, array if limitations if not
397
     */
398
    public function hasAccess($module, $function, APIUserReference $user = null)
399
    {
400
        // Full access if sudo nesting level is set by {@see sudo()}
401
        if ($this->sudoNestingLevel > 0) {
402
            return true;
403
        }
404
405
        if ($user === null) {
406
            $user = $this->getCurrentUserReference();
407
        }
408
409
        // Uses SPI to avoid triggering permission checks in Role/User service
410
        $permissionSets = array();
411
        $roleDomainMapper = $this->getRoleDomainMapper();
412
        $limitationService = $this->getLimitationService();
413
        $spiRoleAssignments = $this->persistenceHandler->userHandler()->loadRoleAssignmentsByGroupId($user->getUserId(), true);
414
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
415
            $permissionSet = array('limitation' => null, 'policies' => array());
416
417
            $spiRole = $this->persistenceHandler->userHandler()->loadRole($spiRoleAssignment->roleId);
418
            foreach ($spiRole->policies as $spiPolicy) {
419
                if ($spiPolicy->module === '*' && $spiRoleAssignment->limitationIdentifier === null) {
420
                    return true;
421
                }
422
423
                if ($spiPolicy->module !== $module && $spiPolicy->module !== '*') {
424
                    continue;
425
                }
426
427
                if ($spiPolicy->function === '*' && $spiRoleAssignment->limitationIdentifier === null) {
428
                    return true;
429
                }
430
431
                if ($spiPolicy->function !== $function && $spiPolicy->function !== '*') {
432
                    continue;
433
                }
434
435
                if ($spiPolicy->limitations === '*' && $spiRoleAssignment->limitationIdentifier === null) {
436
                    return true;
437
                }
438
439
                $permissionSet['policies'][] = $roleDomainMapper->buildDomainPolicyObject($spiPolicy);
440
            }
441
442
            if (!empty($permissionSet['policies'])) {
443
                if ($spiRoleAssignment->limitationIdentifier !== null) {
444
                    $permissionSet['limitation'] = $limitationService
445
                        ->getLimitationType($spiRoleAssignment->limitationIdentifier)
446
                        ->buildValue($spiRoleAssignment->values);
0 ignored issues
show
Bug introduced by
It seems like $spiRoleAssignment->values can also be of type null; however, eZ\Publish\SPI\Limitation\Type::buildValue() does only seem to accept array<integer,*>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
447
                }
448
449
                $permissionSets[] = $permissionSet;
450
            }
451
        }
452
453
        if (!empty($permissionSets)) {
454
            return $permissionSets;
455
        }
456
457
        return false;// No policies matching $module and $function, or they contained limitations
458
    }
459
460
    /**
461
     * Check if user has access to a given action on a given value object.
462
     *
463
     * Indicates if the current user is allowed to perform an action given by the function on the given
464
     * objects.
465
     *
466
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid
467
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported
468
     *
469
     * @param string $module The module, aka controller identifier to check permissions on
470
     * @param string $function The function, aka the controller action to check permissions on
471
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object The object to check if the user has access to
472
     * @param mixed $targets The location, parent or "assignment" value object, or an array of the same
473
     *
474
     * @return bool
475
     */
476
    public function canUser($module, $function, ValueObject $object, $targets = null)
477
    {
478
        $permissionSets = $this->hasAccess($module, $function);
479
        if ($permissionSets === false || $permissionSets === true) {
480
            return $permissionSets;
481
        }
482
483
        if ($targets instanceof ValueObject) {
484
            $targets = array($targets);
485
        } elseif ($targets !== null && !is_array($targets)) {
486
            throw new InvalidArgumentType(
487
                '$targets',
488
                'null|\\eZ\\Publish\\API\\Repository\\Values\\ValueObject|\\eZ\\Publish\\API\\Repository\\Values\\ValueObject[]',
489
                $targets
490
            );
491
        }
492
493
        $limitationService = $this->getLimitationService();
494
        $currentUserRef = $this->getCurrentUserReference();
495
        foreach ($permissionSets as $permissionSet) {
0 ignored issues
show
Bug introduced by
The expression $permissionSets of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
496
            /**
497
             * First deal with Role limitation if any.
498
             *
499
             * Here we accept ACCESS_GRANTED and ACCESS_ABSTAIN, the latter in cases where $object and $targets
500
             * are not supported by limitation.
501
             *
502
             * @var \eZ\Publish\API\Repository\Values\User\Limitation[]
503
             */
504
            if ($permissionSet['limitation'] instanceof Limitation) {
505
                $type = $limitationService->getLimitationType($permissionSet['limitation']->getIdentifier());
506
                $accessVote = $type->evaluate($permissionSet['limitation'], $currentUserRef, $object, $targets);
0 ignored issues
show
Bug introduced by
It seems like $targets defined by parameter $targets on line 476 can also be of type array; however, eZ\Publish\SPI\Limitation\Type::evaluate() does only seem to accept null|array<integer,objec...ry\Values\ValueObject>>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
507
                if ($accessVote === LimitationType::ACCESS_DENIED) {
508
                    continue;
509
                }
510
            }
511
512
            /**
513
             * Loop over all policies.
514
             *
515
             * These are already filtered by hasAccess and given hasAccess did not return boolean
516
             * there must be some, so only return true if one of them says yes.
517
             *
518
             * @var \eZ\Publish\API\Repository\Values\User\Policy
519
             */
520
            foreach ($permissionSet['policies'] as $policy) {
521
                $limitations = $policy->getLimitations();
522
523
                /*
524
                 * Return true if policy gives full access (aka no limitations)
525
                 */
526
                if ($limitations === '*') {
527
                    return true;
528
                }
529
530
                /*
531
                 * Loop over limitations, all must return ACCESS_GRANTED for policy to pass.
532
                 * If limitations was empty array this means same as '*'
533
                 */
534
                $limitationsPass = true;
535
                foreach ($limitations as $limitation) {
536
                    $type = $limitationService->getLimitationType($limitation->getIdentifier());
537
                    $accessVote = $type->evaluate($limitation, $currentUserRef, $object, $targets);
0 ignored issues
show
Bug introduced by
It seems like $targets defined by parameter $targets on line 476 can also be of type array; however, eZ\Publish\SPI\Limitation\Type::evaluate() does only seem to accept null|array<integer,objec...ry\Values\ValueObject>>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
538
                    /*
539
                     * For policy limitation atm only support ACCESS_GRANTED
540
                     *
541
                     * Reasoning: Right now, use of a policy limitation not valid for a policy is per definition a
542
                     * BadState. To reach this you would have to configure the "policyMap" wrongly, like using
543
                     * Node (Location) limitation on state/assign. So in this case Role Limitations will return
544
                     * ACCESS_ABSTAIN (== no access here), and other limitations will throw InvalidArgument above,
545
                     * both cases forcing dev to investigate to find miss configuration. This might be relaxed in
546
                     * the future if valid use cases for ACCESS_ABSTAIN on policy limitations becomes known.
547
                     */
548
                    if ($accessVote !== LimitationType::ACCESS_GRANTED) {
549
                        $limitationsPass = false;
550
                        break;// Break to next policy, all limitations must pass
551
                    }
552
                }
553
                if ($limitationsPass) {
554
                    return true;
555
                }
556
            }
557
        }
558
559
        return false;// None of the limitation sets wanted to let you in, sorry!
560
    }
561
562
    /**
563
     * Get Content Service.
564
     *
565
     * Get service object to perform operations on Content objects and it's aggregate members.
566
     *
567
     * @return \eZ\Publish\API\Repository\ContentService
568
     */
569 View Code Duplication
    public function getContentService()
570
    {
571
        if ($this->contentService !== null) {
572
            return $this->contentService;
573
        }
574
575
        $this->contentService = new ContentService(
576
            $this,
577
            $this->persistenceHandler,
578
            $this->getDomainMapper(),
579
            $this->getRelationProcessor(),
580
            $this->getNameSchemaService(),
581
            $this->getFieldTypeRegistry(),
582
            $this->serviceSettings['content']
583
        );
584
585
        return $this->contentService;
586
    }
587
588
    /**
589
     * Get Content Language Service.
590
     *
591
     * Get service object to perform operations on Content language objects
592
     *
593
     * @return \eZ\Publish\API\Repository\LanguageService
594
     */
595
    public function getContentLanguageService()
596
    {
597
        if ($this->languageService !== null) {
598
            return $this->languageService;
599
        }
600
601
        $this->languageService = new LanguageService(
602
            $this,
603
            $this->persistenceHandler->contentLanguageHandler(),
604
            $this->serviceSettings['language']
605
        );
606
607
        return $this->languageService;
608
    }
609
610
    /**
611
     * Get Content Type Service.
612
     *
613
     * Get service object to perform operations on Content Type objects and it's aggregate members.
614
     * ( Group, Field & FieldCategory )
615
     *
616
     * @return \eZ\Publish\API\Repository\ContentTypeService
617
     */
618 View Code Duplication
    public function getContentTypeService()
619
    {
620
        if ($this->contentTypeService !== null) {
621
            return $this->contentTypeService;
622
        }
623
624
        $this->contentTypeService = new ContentTypeService(
625
            $this,
626
            $this->persistenceHandler->contentTypeHandler(),
627
            $this->getDomainMapper(),
628
            $this->getContentTypeDomainMapper(),
629
            $this->getFieldTypeRegistry(),
630
            $this->serviceSettings['contentType']
631
        );
632
633
        return $this->contentTypeService;
634
    }
635
636
    /**
637
     * Get Content Location Service.
638
     *
639
     * Get service object to perform operations on Location objects and subtrees
640
     *
641
     * @return \eZ\Publish\API\Repository\LocationService
642
     */
643
    public function getLocationService()
644
    {
645
        if ($this->locationService !== null) {
646
            return $this->locationService;
647
        }
648
649
        $this->locationService = new LocationService(
650
            $this,
651
            $this->persistenceHandler,
652
            $this->getDomainMapper(),
653
            $this->getNameSchemaService(),
654
            $this->getPermissionsCriterionHandler(),
655
            $this->serviceSettings['location']
656
        );
657
658
        return $this->locationService;
659
    }
660
661
    /**
662
     * Get Content Trash service.
663
     *
664
     * Trash service allows to perform operations related to location trash
665
     * (trash/untrash, load/list from trash...)
666
     *
667
     * @return \eZ\Publish\API\Repository\TrashService
668
     */
669
    public function getTrashService()
670
    {
671
        if ($this->trashService !== null) {
672
            return $this->trashService;
673
        }
674
675
        $this->trashService = new TrashService(
676
            $this,
677
            $this->persistenceHandler,
678
            $this->getNameSchemaService(),
679
            $this->serviceSettings['trash']
680
        );
681
682
        return $this->trashService;
683
    }
684
685
    /**
686
     * Get Content Section Service.
687
     *
688
     * Get Section service that lets you manipulate section objects
689
     *
690
     * @return \eZ\Publish\API\Repository\SectionService
691
     */
692
    public function getSectionService()
693
    {
694
        if ($this->sectionService !== null) {
695
            return $this->sectionService;
696
        }
697
698
        $this->sectionService = new SectionService(
699
            $this,
700
            $this->persistenceHandler->sectionHandler(),
701
            $this->serviceSettings['section']
702
        );
703
704
        return $this->sectionService;
705
    }
706
707
    /**
708
     * Get User Service.
709
     *
710
     * Get service object to perform operations on Users and UserGroup
711
     *
712
     * @return \eZ\Publish\API\Repository\UserService
713
     */
714
    public function getUserService()
715
    {
716
        if ($this->userService !== null) {
717
            return $this->userService;
718
        }
719
720
        $this->userService = new UserService(
721
            $this,
722
            $this->persistenceHandler->userHandler(),
723
            $this->serviceSettings['user']
724
        );
725
726
        return $this->userService;
727
    }
728
729
    /**
730
     * Get URLAliasService.
731
     *
732
     * @return \eZ\Publish\API\Repository\URLAliasService
733
     */
734
    public function getURLAliasService()
735
    {
736
        if ($this->urlAliasService !== null) {
737
            return $this->urlAliasService;
738
        }
739
740
        $this->urlAliasService = new URLAliasService(
741
            $this,
742
            $this->persistenceHandler->urlAliasHandler(),
743
            $this->serviceSettings['urlAlias']
744
        );
745
746
        return $this->urlAliasService;
747
    }
748
749
    /**
750
     * Get URLWildcardService.
751
     *
752
     * @return \eZ\Publish\API\Repository\URLWildcardService
753
     */
754
    public function getURLWildcardService()
755
    {
756
        if ($this->urlWildcardService !== null) {
757
            return $this->urlWildcardService;
758
        }
759
760
        $this->urlWildcardService = new URLWildcardService(
761
            $this,
762
            $this->persistenceHandler->urlWildcardHandler(),
763
            $this->serviceSettings['urlWildcard']
764
        );
765
766
        return $this->urlWildcardService;
767
    }
768
769
    /**
770
     * Get ObjectStateService.
771
     *
772
     * @return \eZ\Publish\API\Repository\ObjectStateService
773
     */
774
    public function getObjectStateService()
775
    {
776
        if ($this->objectStateService !== null) {
777
            return $this->objectStateService;
778
        }
779
780
        $this->objectStateService = new ObjectStateService(
781
            $this,
782
            $this->persistenceHandler->objectStateHandler(),
783
            $this->serviceSettings['objectState']
784
        );
785
786
        return $this->objectStateService;
787
    }
788
789
    /**
790
     * Get RoleService.
791
     *
792
     * @return \eZ\Publish\API\Repository\RoleService
793
     */
794
    public function getRoleService()
795
    {
796
        if ($this->roleService !== null) {
797
            return $this->roleService;
798
        }
799
800
        $this->roleService = new RoleService(
801
            $this,
802
            $this->persistenceHandler->userHandler(),
803
            $this->getLimitationService(),
804
            $this->getRoleDomainMapper(),
805
            $this->serviceSettings['role']
806
        );
807
808
        return $this->roleService;
809
    }
810
811
    /**
812
     * Get LimitationService.
813
     *
814
     * @return \eZ\Publish\Core\Repository\Helper\LimitationService
815
     */
816
    protected function getLimitationService()
817
    {
818
        if ($this->limitationService !== null) {
819
            return $this->limitationService;
820
        }
821
822
        $this->limitationService = new Helper\LimitationService($this->serviceSettings['role']);
823
824
        return $this->limitationService;
825
    }
826
827
    /**
828
     * Get RoleDomainMapper.
829
     *
830
     * @return \eZ\Publish\Core\Repository\Helper\RoleDomainMapper
831
     */
832
    protected function getRoleDomainMapper()
833
    {
834
        if ($this->roleDomainMapper !== null) {
835
            return $this->roleDomainMapper;
836
        }
837
838
        $this->roleDomainMapper = new Helper\RoleDomainMapper($this->getLimitationService());
839
840
        return $this->roleDomainMapper;
841
    }
842
843
    /**
844
     * Get SearchService.
845
     *
846
     * @return \eZ\Publish\API\Repository\SearchService
847
     */
848
    public function getSearchService()
849
    {
850
        if ($this->searchService !== null) {
851
            return $this->searchService;
852
        }
853
854
        $this->searchService = new SearchService(
855
            $this,
856
            $this->searchHandler,
857
            $this->getDomainMapper(),
858
            $this->getPermissionsCriterionHandler(),
859
            $this->serviceSettings['search']
860
        );
861
862
        return $this->searchService;
863
    }
864
865
    /**
866
     * Get FieldTypeService.
867
     *
868
     * @return \eZ\Publish\API\Repository\FieldTypeService
869
     */
870
    public function getFieldTypeService()
871
    {
872
        if ($this->fieldTypeService !== null) {
873
            return $this->fieldTypeService;
874
        }
875
876
        $this->fieldTypeService = new FieldTypeService($this->getFieldTypeRegistry());
877
878
        return $this->fieldTypeService;
879
    }
880
881
    /**
882
     * @return Helper\FieldTypeRegistry
883
     */
884
    protected function getFieldTypeRegistry()
885
    {
886
        if ($this->fieldTypeRegistry !== null) {
887
            return $this->fieldTypeRegistry;
888
        }
889
890
        $this->fieldTypeRegistry = new Helper\FieldTypeRegistry($this->serviceSettings['fieldType']);
891
892
        return $this->fieldTypeRegistry;
893
    }
894
895
    /**
896
     * @return Helper\NameableFieldTypeRegistry
897
     */
898
    protected function getNameableFieldTypeRegistry()
899
    {
900
        if ($this->nameableFieldTypeRegistry !== null) {
901
            return $this->nameableFieldTypeRegistry;
902
        }
903
904
        $this->nameableFieldTypeRegistry = new Helper\NameableFieldTypeRegistry($this->serviceSettings['nameableFieldTypes']);
905
906
        return $this->nameableFieldTypeRegistry;
907
    }
908
909
    /**
910
     * Get NameSchemaResolverService.
911
     *
912
     *
913
     * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory.
914
     *
915
     * @internal
916
     * @private
917
     *
918
     * @return \eZ\Publish\Core\Repository\Helper\NameSchemaService
919
     */
920
    public function getNameSchemaService()
921
    {
922
        if ($this->nameSchemaService !== null) {
923
            return $this->nameSchemaService;
924
        }
925
926
        $this->nameSchemaService = new Helper\NameSchemaService(
927
            $this->persistenceHandler->contentTypeHandler(),
928
            $this->getContentTypeDomainMapper(),
929
            $this->getNameableFieldTypeRegistry(),
930
            $this->serviceSettings['nameSchema']
931
        );
932
933
        return $this->nameSchemaService;
934
    }
935
936
    /**
937
     * Get RelationProcessor.
938
     *
939
     *
940
     * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory.
941
     *
942
     * @return \eZ\Publish\Core\Repository\Helper\RelationProcessor
943
     */
944
    protected function getRelationProcessor()
945
    {
946
        if ($this->relationProcessor !== null) {
947
            return $this->relationProcessor;
948
        }
949
950
        $this->relationProcessor = new Helper\RelationProcessor($this->persistenceHandler);
951
952
        return $this->relationProcessor;
953
    }
954
955
    /**
956
     * Get Content Domain Mapper.
957
     *
958
     * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory.
959
     *
960
     * @return \eZ\Publish\Core\Repository\Helper\DomainMapper
961
     */
962
    protected function getDomainMapper()
963
    {
964
        if ($this->domainMapper !== null) {
965
            return $this->domainMapper;
966
        }
967
968
        $this->domainMapper = new Helper\DomainMapper(
969
            $this->persistenceHandler->contentHandler(),
970
            $this->persistenceHandler->locationHandler(),
971
            $this->persistenceHandler->contentTypeHandler(),
972
            $this->persistenceHandler->contentLanguageHandler(),
973
            $this->getFieldTypeRegistry()
974
        );
975
976
        return $this->domainMapper;
977
    }
978
979
    /**
980
     * Get ContentType Domain Mapper.
981
     *
982
     * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory.
983
     *
984
     * @return \eZ\Publish\Core\Repository\Helper\ContentTypeDomainMapper
985
     */
986
    protected function getContentTypeDomainMapper()
987
    {
988
        if ($this->contentTypeDomainMapper !== null) {
989
            return $this->contentTypeDomainMapper;
990
        }
991
992
        $this->contentTypeDomainMapper = new Helper\ContentTypeDomainMapper(
993
            $this->persistenceHandler->contentLanguageHandler(),
994
            $this->getFieldTypeRegistry()
995
        );
996
997
        return $this->contentTypeDomainMapper;
998
    }
999
1000
    /**
1001
     * Get PermissionsCriterionHandler.
1002
     *
1003
     *
1004
     * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory.
1005
     *
1006
     * @return \eZ\Publish\Core\Repository\PermissionsCriterionHandler
1007
     */
1008
    protected function getPermissionsCriterionHandler()
1009
    {
1010
        return $this->permissionsCriterionHandler !== null ?
1011
            $this->permissionsCriterionHandler :
1012
            $this->permissionsCriterionHandler = new PermissionsCriterionHandler($this);
1013
    }
1014
1015
    /**
1016
     * Begin transaction.
1017
     *
1018
     * Begins an transaction, make sure you'll call commit or rollback when done,
1019
     * otherwise work will be lost.
1020
     */
1021
    public function beginTransaction()
1022
    {
1023
        $this->persistenceHandler->beginTransaction();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\SPI\Persisten...ler::beginTransaction() has been deprecated with message: Since 5.3 {@use transactionHandler()->beginTransaction()}

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1024
1025
        ++$this->transactionDepth;
1026
        $this->commitEventsQueue[++$this->transactionCount] = array();
1027
    }
1028
1029
    /**
1030
     * Commit transaction.
1031
     *
1032
     * Commit transaction, or throw exceptions if no transactions has been started.
1033
     *
1034
     * @throws RuntimeException If no transaction has been started
1035
     */
1036
    public function commit()
1037
    {
1038
        try {
1039
            $this->persistenceHandler->commit();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\SPI\Persistence\Handler::commit() has been deprecated with message: Since 5.3 {@use transactionHandler()->commit()}

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1040
1041
            --$this->transactionDepth;
1042
1043
            if ($this->transactionDepth === 0) {
1044
                $queueCountDown = count($this->commitEventsQueue);
1045
                foreach ($this->commitEventsQueue as $eventsQueue) {
1046
                    --$queueCountDown;
1047
                    if (empty($eventsQueue)) {
1048
                        continue;
1049
                    }
1050
1051
                    $eventCountDown = count($eventsQueue);
1052
                    foreach ($eventsQueue as $event) {
1053
                        --$eventCountDown;
1054
                        // event expects a boolean param, if true it means it is last event (for commit use)
1055
                        $event($queueCountDown === 0 && $eventCountDown === 0);
1056
                    }
1057
                }
1058
1059
                $this->commitEventsQueue = array();
1060
            }
1061
        } catch (Exception $e) {
1062
            throw new RuntimeException($e->getMessage(), 0, $e);
1063
        }
1064
    }
1065
1066
    /**
1067
     * Rollback transaction.
1068
     *
1069
     * Rollback transaction, or throw exceptions if no transactions has been started.
1070
     *
1071
     * @throws RuntimeException If no transaction has been started
1072
     */
1073
    public function rollback()
1074
    {
1075
        try {
1076
            $this->persistenceHandler->rollback();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\SPI\Persistence\Handler::rollback() has been deprecated with message: Since 5.3 {@use transactionHandler()->rollback()}

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1077
1078
            --$this->transactionDepth;
1079
            unset($this->commitEventsQueue[$this->transactionCount]);
1080
        } catch (Exception $e) {
1081
            throw new RuntimeException($e->getMessage(), 0, $e);
1082
        }
1083
    }
1084
1085
    /**
1086
     * Enqueue an event to be triggered at commit or directly if no transaction has started.
1087
     *
1088
     * @param Callable $event
1089
     */
1090
    public function commitEvent($event)
1091
    {
1092
        if ($this->transactionDepth !== 0) {
1093
            $this->commitEventsQueue[$this->transactionCount][] = $event;
1094
        } else {
1095
            // event expects a boolean param, if true it means it is last event (for commit use)
1096
            $event(true);
1097
        }
1098
    }
1099
1100
    /**
1101
     * Only for internal use.
1102
     *
1103
     * Creates a \DateTime object for $timestamp in the current time zone
1104
     *
1105
     * @param int $timestamp
1106
     *
1107
     * @return \DateTime
1108
     */
1109 View Code Duplication
    public function createDateTime($timestamp = null)
1110
    {
1111
        $dateTime = new \DateTime();
1112
        if ($timestamp !== null) {
1113
            $dateTime->setTimestamp($timestamp);
1114
        }
1115
1116
        return $dateTime;
1117
    }
1118
}
1119