Completed
Push — master ( ce4cd5...ff0a6e )
by Joschi
03:16
created

RelationFactory::parseRelationString()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 54
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 8.0231

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 8
eloc 27
c 5
b 0
f 0
nc 12
nop 2
dl 0
loc 54
ccs 26
cts 28
cp 0.9286
crap 8.0231
rs 7.4119

How to fix   Long Method   

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
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\<Layer>
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Apparat\Object\Domain\Factory;
38
39
use Apparat\Kernel\Ports\Kernel;
40
use Apparat\Object\Domain\Model\Path\ApparatUrl;
41
use Apparat\Object\Domain\Model\Path\Url;
42
use Apparat\Object\Domain\Model\Relation\ContributedByRelation;
43
use Apparat\Object\Domain\Model\Relation\ContributesRelation;
44
use Apparat\Object\Domain\Model\Relation\EmbeddedByRelation;
45
use Apparat\Object\Domain\Model\Relation\EmbedsRelation;
46
use Apparat\Object\Domain\Model\Relation\InvalidArgumentException;
47
use Apparat\Object\Domain\Model\Relation\LikedByRelation;
48
use Apparat\Object\Domain\Model\Relation\LikesRelation;
49
use Apparat\Object\Domain\Model\Relation\OutOfBoundsException;
50
use Apparat\Object\Domain\Model\Relation\ReferredByRelation;
51
use Apparat\Object\Domain\Model\Relation\RefersToRelation;
52
use Apparat\Object\Domain\Model\Relation\RelationInterface;
53
use Apparat\Object\Domain\Model\Relation\RepliedByRelation;
54
use Apparat\Object\Domain\Model\Relation\RepliesToRelation;
55
use Apparat\Object\Domain\Model\Relation\RepostedByRelation;
56
use Apparat\Object\Domain\Model\Relation\RepostsRelation;
57
use Apparat\Object\Domain\Repository\RepositoryInterface;
58
use Apparat\Object\Infrastructure\Utilities\Validator;
59
60
/**
61
 * Relation factory
62
 *
63
 * @package Apparat\Object
64
 * @subpackage Apparat\Object\Domain
65
 */
66
class RelationFactory
67
{
68
    /**
69
     * URL component key
70
     *
71
     * @string
72
     */
73
    const PARSE_URL = 'url';
74
    /**
75
     * Label component key
76
     *
77
     * @string
78
     */
79
    const PARSE_LABEL = 'label';
80
    /**
81
     * Email component key
82
     *
83
     * @string
84
     */
85
    const PARSE_EMAIL = 'email';
86
    /**
87
     * Component relation coupling
88
     *
89
     * @string
90
     */
91
    const PARSE_COUPLING = 'coupling';
92
    /**
93
     * Relation types
94
     *
95
     * @var array
96
     */
97
    public static $relationTypes = [
98
        ContributesRelation::TYPE => ContributesRelation::class,
99
        ContributedByRelation::TYPE => ContributedByRelation::class,
100
        EmbedsRelation::TYPE => EmbedsRelation::class,
101
        EmbeddedByRelation::TYPE => EmbeddedByRelation::class,
102
        LikesRelation::TYPE => LikesRelation::class,
103
        LikedByRelation::TYPE => LikedByRelation::class,
104
        RefersToRelation::TYPE => RefersToRelation::class,
105
        ReferredByRelation::TYPE => ReferredByRelation::class,
106
        RepliesToRelation::TYPE => RepliesToRelation::class,
107
        RepliedByRelation::TYPE => RepliedByRelation::class,
108
        RepostsRelation::TYPE => RepostsRelation::class,
109
        RepostedByRelation::TYPE => RepostedByRelation::class,
110
    ];
111
112
    /**
113
     * Parse a relation serialization and instantiate the relation
114
     *
115
     * @param string $relationType Relation type
116
     * @param string $relation Relation serialization
117
     * @param RepositoryInterface $contextRepository Context repository
118
     * @return RelationInterface Relation object
119
     */
120 3
    public static function createFromString($relationType, $relation, RepositoryInterface $contextRepository)
121
    {
122
        // Validate the relation type
123 3
        self::validateRelationType($relationType);
124
125
        // Create the relation instance
126 3
        return Kernel::create(
127 3
            self::$relationTypes[$relationType],
128 3
            array_values(self::parseRelationString($relation, $contextRepository))
129
        );
130
    }
131
132
    /**
133
     * Validate a relation type
134
     *
135
     * @param string $relationType Relation type
136
     * @throws InvalidArgumentException If the relation type is invalid
137
     */
138 6
    public static function validateRelationType($relationType)
139
    {
140
        // If the relation type is invalid
141 6
        if (empty($relationType) || empty(self::$relationTypes[$relationType])) {
142
            throw new OutOfBoundsException(
143
                sprintf('Invalid object relation type "%s"', $relationType),
144
                OutOfBoundsException::INVALID_OBJECT_RELATION_TYPE
145
            );
146
        }
147 6
    }
148
149
    /**
150
     * Parse a relation serialization and instantiate the relation object
151
     *
152
     * @param string $relation Relation serialization
153
     * @param RepositoryInterface $contextRepository Context repository
154
     * @return array Parsed relation components
155
     * @throws InvalidArgumentException If the email component has already been registered
156
     * @throws InvalidArgumentException If the URL component has already been registered
157
     */
158 3
    protected static function parseRelationString($relation, RepositoryInterface $contextRepository)
159
    {
160
        $parsed = [
161 3
            self::PARSE_URL => null,
162 3
            self::PARSE_LABEL => null,
163 3
            self::PARSE_EMAIL => null,
164 3
            self::PARSE_COUPLING => RelationInterface::LOOSE_COUPLING,
165 3
        ];
166
167
        // Split the relation string and parse the components
168 3
        foreach (preg_split('%\s+%', $relation) as $relationComponent) {
169
            // If it's an email component
170 3
            if (!strncmp('<', $relationComponent, 1)) {
171
                // If the email component has already been registered
172 2
                if (!empty($parsed[self::PARSE_EMAIL])) {
173 1
                    throw new InvalidArgumentException(
174 1
                        sprintf('Repeated relation email component "%s" not allowed', self::PARSE_EMAIL),
175
                        InvalidArgumentException::REPEATED_RELATION_COMPONENT_NOT_ALLOWED
176 1
                    );
177
                }
178
179 2
                $parsed[self::PARSE_EMAIL] = self::parseRelationEmail($relationComponent);
180 1
                continue;
181
            }
182
183
            // Next: Try to parse it as URL
184
            try {
185 1
                $url = self::parseRelationUrl($relationComponent, $parsed[self::PARSE_COUPLING], $contextRepository);
0 ignored issues
show
Documentation introduced by
$parsed[self::PARSE_COUPLING] is of type null|string|object<Appar...\Domain\Model\Path\Url>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
186
187
                // If the URL component has already been registered
188 1
                if (!empty($parsed[self::PARSE_URL])) {
189 1
                    throw new InvalidArgumentException(
190 1
                        sprintf('Repeated relation url component "%s" not allowed', self::PARSE_URL),
191
                        InvalidArgumentException::REPEATED_RELATION_COMPONENT_NOT_ALLOWED
192 1
                    );
193
                }
194
195 1
                $parsed[self::PARSE_URL] = $url;
196
197
                // Else: Process as label component
198 1
            } catch (\Exception $e) {
199
                // If it's a repeated URL component
200 1
                if (($e instanceof InvalidArgumentException)
201 1
                    && ($e->getCode() == InvalidArgumentException::REPEATED_RELATION_COMPONENT_NOT_ALLOWED)
202 1
                ) {
203 1
                    throw $e;
204
                }
205
206
                $parsed[self::PARSE_LABEL] = trim($parsed[self::PARSE_LABEL].' '.$relationComponent);
207
            }
208 1
        }
209
210
        return $parsed;
211
    }
212
213
    /**
214
     * Parse and validate a relation email address component
215
     *
216
     * @param string $relationEmail Email address
217
     * @return string Valid email address
218
     * @throws InvalidArgumentException If the email address is invalid
219
     */
220 2
    protected static function parseRelationEmail($relationEmail)
221
    {
222
        // If it's a valid email address
223 2
        if (preg_match('%^\<(.+)\>$%', $relationEmail, $emailAddress) && Validator::isEmail($emailAddress[1])) {
224 1
            return $emailAddress[1];
225
        }
226
227 1
        throw new InvalidArgumentException(
228 1
            sprintf('Invalid relation email address "%s"', $relationEmail),
229
            InvalidArgumentException::INVALID_RELATION_EMAIL_ADDRESS
230 1
        );
231
    }
232
233
    /**
234
     * Parse and instantiate a relation URL
235
     *
236
     * @param string $url URL
237
     * @param int $coupling Strong coupling
238
     * @param RepositoryInterface $contextRepository Context repository
239
     * @return Url URL
240
     * @throws InvalidArgumentException If the relation URL is invalid
241
     */
242 1
    protected static function parseRelationUrl($url, &$coupling, RepositoryInterface $contextRepository)
243
    {
244 1
        if (strlen($url)) {
245
            // If the URL requires tight coupling
246 1
            if (!strncmp('!', $url, 1)) {
247
                $coupling = RelationInterface::TIGHT_COUPLING;
248
                $url = substr($url, 1);
249
            }
250
251
            // Try to instantiate as an apparat URL
252
            try {
253 1
                return Kernel::create(ApparatUrl::class, [$url, true, $contextRepository]);
254
255
                // If there's an apparat URL problem: Try to instantiate as a regular URL
256 1
            } catch (\Apparat\Object\Domain\Model\Path\InvalidArgumentException $e) {
257
                /** @var Url $urlInstance */
258 1
                $urlInstance = Kernel::create(Url::class, [$url]);
259 1
                if ($urlInstance->isAbsolute()) {
260 1
                    return $urlInstance;
261
                }
262
            }
263
        }
264
265
        throw new InvalidArgumentException(
266
            sprintf('Invalid relation URL "%s"', $url),
267
            InvalidArgumentException::INVALID_RELATION_URL
268
        );
269
    }
270
}
271