Completed
Push — tmp-dev ( 91d361...932be6 )
by Romain
02:30
created

ParentsTrait::alongParents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/*
3
 * 2017 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 Configuration Object project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\ConfigurationObject\Service\Items\Parents;
15
16
use Romm\ConfigurationObject\Exceptions\DuplicateEntryException;
17
use Romm\ConfigurationObject\Exceptions\EntryNotFoundException;
18
use Romm\ConfigurationObject\Exceptions\InvalidTypeException;
19
use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21
/**
22
 * Use this trait in your configuration objects (it will work only if they do
23
 * use the service `ParentsService`).
24
 *
25
 * It will store the parent objects of the current object.
26
 */
27
trait ParentsTrait
28
{
29
30
    /**
31
     * Note: must be private, or the TYPO3 reflection services will go in an
32
     * infinite loop.
33
     *
34
     * @var object[]
35
     */
36
    private $_parents = [];
37
38
    /**
39
     * @param object[] $parents
40
     *
41
     * @deprecated This function is deprecated and will be removed in v2!
42
     *             Use function `addParents()` instead.
43
     */
44
    public function setParents(array $parents)
45
    {
46
        GeneralUtility::logDeprecatedFunction();
47
48
        $this->_parents = $parents;
49
    }
50
51
    /**
52
     * @param object $parent
53
     * @param bool   $direct If true, the parent will be added as the direct (closest) parent of this object.
54
     * @return $this
55
     * @throws DuplicateEntryException
56
     * @throws InvalidTypeException
57
     */
58
    public function attachParent($parent, $direct = true)
59
    {
60
        if (false === is_object($parent)) {
61
            throw new InvalidTypeException(
62
                'The parent must be an object, "' . gettype($parent) . '" was given.',
63
                1493804124
64
            );
65
        }
66
67
        foreach ($this->_parents as $parentItem) {
68
            if ($parent === $parentItem) {
69
                throw new DuplicateEntryException(
70
                    'The given parent (' . get_class($parent) . ') was already attached to this object (' . get_class($this) . ').',
71
                    1493804518
72
                );
73
            }
74
        }
75
76
        if (true === $direct) {
77
            array_unshift($this->_parents, $parent);
78
        } else {
79
            array_push($this->_parents, $parent);
80
        }
81
82
        return $this;
83
    }
84
85
    /**
86
     * Loops on each given parent and attach it to this object.
87
     *
88
     * The order matters: the first item will be added as a direct parent
89
     * whereas the last one will be the remote parent.
90
     *
91
     * Note that this function will also reset
92
     *
93
     * @param object[] $parents
94
     */
95
    public function attachParents(array $parents)
96
    {
97
        $this->_parents = [];
98
99
        foreach ($parents as $parent) {
100
            $this->attachParent($parent, false);
101
        }
102
    }
103
104
    /**
105
     * Will loop along each parent of this object, and every parent of the
106
     * parents: the given callback is called with a single parameter which is
107
     * the current parent.
108
     *
109
     * When the callback returns `false`, the loop breaks.
110
     *
111
     * @param callable $callback
112
     */
113
    public function alongParents(callable $callback)
114
    {
115
        ParentsRecursiveService::get()->alongParents($callback, $this, $this->_parents);
116
    }
117
118
    /**
119
     * Returns true if the class has a given parent.
120
     *
121
     * @param string $parentClassName Name of the parent class.
122
     * @return bool
123
     */
124
    public function hasParent($parentClassName)
125
    {
126
        $found = false;
127
128
        $this->alongParents(function ($parent) use ($parentClassName, &$found) {
129
            if ($parent instanceof $parentClassName) {
130
                $found = true;
131
132
                return false;
133
            }
134
135
            return true;
136
        });
137
138
        return $found;
139
    }
140
141
    /**
142
     * Will fetch the first parent which matches the given class name.
143
     *
144
     * If a parent is found, then `$callback` is called, and its returned value
145
     * is returned by this function.
146
     *
147
     * If no parent is found, then `$notFoundCallBack` is called if it was
148
     * defined.
149
     *
150
     * @param string   $parentClassName  Name of the class name of the wanted parent.
151
     * @param callable $callback         A closure which will be called if the parent is found.
152
     * @param callable $notFoundCallback A closure which is called if the parent is not found.
153
     * @return mixed|null
154
     */
155
    public function withFirstParent($parentClassName, callable $callback, callable $notFoundCallback = null)
156
    {
157
        $result = null;
158
159
        if ($this->hasParent($parentClassName)) {
160
            $parent = $this->getFirstParent($parentClassName);
161
            $result = call_user_func($callback, $parent);
162
        } elseif (null !== $notFoundCallback) {
163
            $result = call_user_func($notFoundCallback);
164
        }
165
166
        return $result;
167
    }
168
169
    /**
170
     * Returns the first found instance of the desired parent.
171
     *
172
     * An exception is thrown if the parent is not found. It is advised to use
173
     * the function `hasParent()` before using this function.
174
     *
175
     * @param string $parentClassName Name of the parent class.
176
     * @return object
177
     * @throws EntryNotFoundException
178
     */
179
    public function getFirstParent($parentClassName)
180
    {
181
        $foundParent = null;
182
183
        $this->alongParents(function ($parent) use ($parentClassName, &$foundParent) {
184
            if ($parent instanceof $parentClassName) {
185
                $foundParent = $parent;
186
187
                return false;
188
            }
189
190
            return true;
191
        });
192
193
        if (null === $foundParent) {
194
            throw new EntryNotFoundException(
195
                'The parent "' . $parentClassName . '" was not found in this object (class "' . get_class($this) . '"). Use the function "hasParent()" before your call to this function!',
196
                1471379635
197
            );
198
        }
199
200
        return $foundParent;
201
    }
202
}
203