Completed
Push — master ( c434f2...7ac5be )
by Sebastian
03:19
created

Names::render()   C

Complexity

Conditions 17
Paths 90

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 36
nc 90
nop 1
dl 0
loc 61
rs 6.1902
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * citeproc-php
4
 *
5
 * @link        http://github.com/seboettg/citeproc-php for the source repository
6
 * @copyright   Copyright (c) 2016 Sebastian Böttger.
7
 * @license     https://opensource.org/licenses/MIT
8
 */
9
10
namespace Seboettg\CiteProc\Rendering\Name;
11
12
use Seboettg\CiteProc\Rendering\Label;
13
use Seboettg\CiteProc\Rendering\RenderingInterface;
14
use Seboettg\CiteProc\Styles\AffixesTrait;
15
use Seboettg\CiteProc\Styles\DelimiterTrait;
16
use Seboettg\CiteProc\Styles\FormattingTrait;
17
use Seboettg\CiteProc\Util\Factory;
18
use Seboettg\Collection\ArrayList;
19
20
21
/**
22
 * Class Group
23
 * @package Seboettg\CiteProc\Rendering
24
 *
25
 * @author Sebastian Böttger <[email protected]>
26
 */
27
class Names implements RenderingInterface
28
{
29
    use DelimiterTrait;
30
    use AffixesTrait;
31
    use FormattingTrait;
32
33
    /**
34
     * Variables (selected with the required variable attribute), each of which can contain multiple names (e.g. the
35
     * “author” variable contains all the author names of the cited item). If multiple variables are selected
36
     * (separated by single spaces, see example below), each variable is independently rendered in the order specified.
37
     *
38
     * @var ArrayList
39
     */
40
    private $variables;
41
42
    /**
43
     * The Name element, an optional child element of Names, can be used to describe the formatting of individual
44
     * names, and the separation of names within a name variable.
45
     *
46
     * @var Name
47
     */
48
    private $name;
49
50
    /**
51
     * The optional Label element must be included after the Name and EtAl elements, but before
52
     * the Substitute element. When used as a child element of Names, Label does not carry the variable
53
     * attribute; it uses the variable(s) set on the parent Names element instead.
54
     *
55
     * @var Label
56
     */
57
    private $label;
58
59
    /**
60
     * The optional Substitute element, which must be included as the last child element of Names, adds
61
     * substitution in case the name variables specified in the parent cs:names element are empty. The substitutions
62
     * are specified as child elements of Substitute, and must consist of one or more rendering elements (with the
63
     * exception of Layout). A shorthand version of Names without child elements, which inherits the attributes
64
     * values set on the cs:name and EtAl child elements of the original Names element, may also be used. If
65
     * Substitute contains multiple child elements, the first element to return a non-empty result is used for
66
     * substitution. Substituted variables are suppressed in the rest of the output to prevent duplication. An example,
67
     * where an empty “author” name variable is substituted by the “editor” name variable, or, when no editors exist,
68
     * by the “title” macro:
69
     *
70
     * <macro name="author">
71
     *     <names variable="author">
72
     *         <substitute>
73
     *             <names variable="editor"/>
74
     *             <text macro="title"/>
75
     *         </substitute>
76
     *     </names>
77
     * </macro>
78
     *
79
     * @var Substitute
80
     */
81
    private $substitute;
82
83
    /**
84
     * Et-al abbreviation, controlled via the et-al-... attributes (see Name), can be further customized with the
85
     * optional cs:et-al element, which must follow the cs:name element (if present). The term attribute may be set to
86
     * either “et-al” (the default) or to “and others” to use either term. The formatting attributes may also be used,
87
     * for example to italicize the “et-al” term:
88
     *
89
     * @var EtAl
90
     */
91
    private $etAl;
92
93
    /**
94
     * The delimiter attribute may be set on cs:names to separate the names of the different name variables (e.g. the
95
     * semicolon in “Doe, Smith (editors); Johnson (translator)”).
96
     *
97
     * @var string
98
     */
99
    private $delimiter = ", ";
100
101
    /**
102
     * Names constructor.
103
     * @param \SimpleXMLElement $node
104
     */
105
    public function __construct(\SimpleXMLElement $node)
106
    {
107
108
        /** @var \SimpleXMLElement $child */
109
        foreach ($node->children() as $child) {
110
111
            switch ($child->getName()) {
112
                case "name":
113
                    $this->name = Factory::create($child, $this);
114
                    break;
115
                case "label":
116
                    $this->label = Factory::create($child);
117
                    break;
118
                case "substitute":
119
                    $this->substitute = Factory::create($child);
120
                    break;
121
                case "et-al":
122
                    $this->etAl = Factory::create($child);
123
            }
124
        }
125
126
        /** @var \SimpleXMLElement $attribute */
127
        foreach ($node->attributes() as $attribute) {
128
            if ("variable" === $attribute->getName()) {
129
                $this->variables = new ArrayList(explode(" ", (string) $attribute));
130
                break;
131
            }
132
        }
133
134
        $this->initDelimiterAttributes($node);
135
        $this->initAffixesAttributes($node);
136
        $this->initFormattingAttributes($node);
137
    }
138
139
    /**
140
     * This outputs the contents of one or more name variables (selected with the required variable attribute), each
141
     * of which can contain multiple names (e.g. the “author” variable contains all the author names of the cited item).
142
     * If multiple variables are selected (separated by single spaces), each variable is independently rendered in the
143
     * order specified, with one exception: when the selection consists of “editor” and “translator”, and when the
144
     * contents of these two name variables is identical, then the contents of only one name variable is rendered. In
145
     * addition, the “editortranslator” term is used if the Names element contains a Label element, replacing the
146
     * default “editor” and “translator” terms (e.g. resulting in “Doe (editor & translator)”).
147
     *
148
     * @param $data
149
     * @return string
150
     */
151
    public function render($data)
152
    {
153
        $str = "";
154
        $matches = [];
155
156
        /* when the selection consists of “editor” and “translator”, and when the contents of these two name variables
157
        is identical, then the contents of only one name variable is rendered. In addition, the “editortranslator”
158
        term is used if the cs:names element contains a cs:label element, replacing the default “editor” and
159
        “translator” terms (e.g. resulting in “Doe (editor & translator)”) */
160
        if ($this->variables->hasValue("editor") && $this->variables->hasValue("translator")) {
161
            if (isset($data->editor) && isset($data->translator)) {
162
                if (isset($name)) {
0 ignored issues
show
Bug introduced by
The variable $name seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
163
                    $str .= $this->name->render($data->editor);
164
                } else {
165
                    $arr = [];
166
                    foreach ($data->editor as $editor) {
167
                        $arr[] = $this->format($editor->family . ", " . $editor->given);
168
                    }
169
                    $str .= implode($this->delimiter, $arr);
170
                }
171
                if (isset($this->label)) {
172
                    $this->label->setVariable("editortranslator");
173
                    $str .= $this->label->render("");
0 ignored issues
show
Documentation introduced by
'' is of type string, but the function expects a object<stdClass>.

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...
174
                }
175
                $vars = $this->variables->toArray();
176
                $vars = array_filter($vars, function($value) {
177
                    return !($value === "editor" || $value === "translator");
178
                });
179
                $this->variables->setArray($vars);
180
            }
181
        }
182
183
        foreach ($this->variables as $variable) {
184
            if (isset($data->{$variable}) && (!empty($data->{$variable}))) {
185
                $matches[] = $variable;
186
            }
187
        }
188
189
190
191
        $results = [];
192
        foreach ($matches as $var) {
193
194
            if (!empty($data->{$var})) {
195
                if (!empty($this->name)) {
196
                    $name = $this->name->render($data->{$var});
197
                    if (!empty($this->label)) {
198
                        $this->label->setVariable($var);
199
                        $name .= $this->label->render($data);
200
                    }
201
                    $results[] = $this->format($name);
202
                } else {
203
                    foreach ($data->{$var} as $name) {
204
                        $results[] = $this->format($name->given . " " . $name->family);
205
                    }
206
                }
207
            }
208
        }
209
        $str .= implode($this->delimiter, $results);
210
        return $this->addAffixes($str);
211
    }
212
213
    /**
214
     * @return bool
215
     */
216
    public function hasEtAl()
217
    {
218
        return !empty($this->etAl);
219
    }
220
221
    /**
222
     * @return EtAl
223
     */
224
    public function getEtAl()
225
    {
226
        return $this->etAl;
227
    }
228
229
    /**
230
     * @param EtAl $etAl
231
     * @return $this
232
     */
233
    public function setEtAl(EtAl $etAl)
234
    {
235
        $this->etAl = $etAl;
236
        return $this;
237
    }
238
239
    /**
240
     * @return bool
241
     */
242
    public function hasName()
243
    {
244
        return !empty($this->name);
245
    }
246
247
    /**
248
     * @return Name
249
     */
250
    public function getName()
251
    {
252
        return $this->name;
253
    }
254
255
    /**
256
     * @param Name $name
257
     * @return $this
258
     */
259
    public function setName(Name $name)
260
    {
261
        $this->name = $name;
262
        return $this;
263
    }
264
265
    public function getDelimiter()
266
    {
267
        return $this->delimiter;
268
    }
269
270
    public function getVariables()
271
    {
272
        return $this->variables;
273
    }
274
275
}