Test Failed
Push — master ( 9d4e41...1cd52c )
by Sebastian
03:24
created

Date::render()   F

Complexity

Conditions 28
Paths 303

Size

Total Lines 117
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 117
rs 3.4987
c 0
b 0
f 0
cc 28
eloc 62
nc 303
nop 1

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\Date;
11
12
use Seboettg\CiteProc\CiteProc;
13
use Seboettg\CiteProc\Exception\CiteProcException;
14
use Seboettg\CiteProc\Styles\AffixesTrait;
15
use Seboettg\CiteProc\Styles\DisplayTrait;
16
use Seboettg\CiteProc\Styles\FormattingTrait;
17
use Seboettg\CiteProc\Styles\TextCaseTrait;
18
use Seboettg\CiteProc\Util;
19
use Seboettg\Collection\ArrayList;
20
21
22
/**
23
 * Class Date
24
 * @package Seboettg\CiteProc\Rendering
25
 *
26
 * @author Sebastian Böttger <[email protected]>
27
 */
28
class Date
29
{
30
31
    use AffixesTrait,
32
        DisplayTrait,
33
        FormattingTrait,
34
        TextCaseTrait;
35
36
    // ymd
37
    const DATE_RANGE_STATE_NONE         = 0; // 000
38
    const DATE_RANGE_STATE_DAY          = 1; // 001
39
    const DATE_RANGE_STATE_MONTH        = 2; // 010
40
    const DATE_RANGE_STATE_MONTHDAY     = 3; // 011
41
    const DATE_RANGE_STATE_YEAR         = 4; // 100
42
    const DATE_RANGE_STATE_YEARDAY      = 5; // 101
43
    const DATE_RANGE_STATE_YEARMONTH    = 6; // 110
44
    const DATE_RANGE_STATE_YEARMONTHDAY = 7; // 111
45
46
    private static $localizedDateFormats = [
47
        'numeric',
48
        'text'
49
    ];
50
51
    /**
52
     * @var ArrayList
53
     */
54
    private $dateParts;
55
56
    /**
57
     * @var string
58
     */
59
    private $form = "";
60
61
    /**
62
     * @var string
63
     */
64
    private $variable = "";
65
66
    /**
67
     * @var string
68
     */
69
    private $datePartsAttribute = "";
70
71
    public function __construct(\SimpleXMLElement $node)
72
    {
73
        $this->dateParts = new ArrayList();
74
75
        /** @var \SimpleXMLElement $attribute */
76 View Code Duplication
        foreach ($node->attributes() as $attribute) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
77
            switch ($attribute->getName()) {
78
                case 'form':
79
                    $this->form = (string) $attribute;
80
                    break;
81
                case 'variable':
82
                    $this->variable = (string) $attribute;
83
                    break;
84
                case 'date-parts':
85
                    $this->datePartsAttribute = (string) $attribute;
86
            }
87
        }
88
        /** @var \SimpleXMLElement $child */
89
        foreach ($node->children() as $child) {
90
            if ($child->getName() === "date-part") {
91
                $datePartName = (string) $child->attributes()["name"];
92
                $this->dateParts->set($this->form . "-" . $datePartName, Util\Factory::create($child));
93
            }
94
        }
95
96
        $this->initAffixesAttributes($node);
97
        $this->initDisplayAttributes($node);
98
        $this->initFormattingAttributes($node);
99
        $this->initTextCaseAttributes($node);
100
    }
101
102
    /**
103
     * @param $data
104
     * @return string
105
     */
106
    public function render($data)
107
    {
108
        $ret = "";
109
        $var = null;
0 ignored issues
show
Unused Code introduced by
$var is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
110
        if (isset($data->{$this->variable})) {
111
            $var = $data->{$this->variable};
112
        } else {
113
            return "";
114
        }
115
116
        if (!isset($data->{$this->variable}->{'date-parts'}) || empty($data->{$this->variable}->{'date-parts'})) {
117
            if (isset($data->{$this->variable}->raw) && !empty($data->{$this->variable}->raw)) {
118
                try {
119
                    // try to parse date parts from "raw" attribute
120
                    $var->{'date-parts'} = Util\Date::parseDateParts($data->{$this->variable});
121
                } catch (CiteProcException $e) {
122
                    return $this->addAffixes($this->format($this->applyTextCase($data->{$this->variable}->raw)));
123
                }
124
            } else {
125
                return "";
126
            }
127
        }
128
129
        $form = $this->form;
130
        $dateParts = explode("-", $this->datePartsAttribute);
131
132
        /* Localized date formats are selected with the optional form attribute, which must set to either “numeric”
133
        (for fully numeric formats, e.g. “12-15-2005”), or “text” (for formats with a non-numeric month, e.g.
134
        “December 15, 2005”). Localized date formats can be customized in two ways. First, the date-parts attribute may
135
        be used to show fewer date parts. The possible values are:
136
            - “year-month-day” - (default), renders the year, month and day
137
            - “year-month” - renders the year and month
138
            - “year” - renders the year */
139
140
        if ($this->dateParts->count() < 1 && in_array($form, self::$localizedDateFormats)) {
141
            if ($this->hasDatePartsFromLocales($form)) {
142
                $datePartsFromLocales = $this->getDatePartsFromLocales($form);
143
                array_filter($datePartsFromLocales, function (\SimpleXMLElement $item) use ($dateParts) {
144
                    return in_array($item["name"], $dateParts);
145
                });
146
147
                foreach ($datePartsFromLocales as $datePartNode) {
148
                    $datePart = $datePartNode["name"];
149
                    $this->dateParts->set("$form-$datePart", Util\Factory::create($datePartNode));
150
                }
151
            } else { //otherwise create default date parts
152
                foreach ($dateParts as $datePart) {
153
                    $this->dateParts->add("$form-$datePart", new DatePart(new \SimpleXMLElement('<date-part name="' . $datePart . '" form="' . $form . '" />')));
154
                }
155
            }
156
        }
157
158
159
        // No date-parts in date-part attribute defined, take into account that the defined date-part children will be used.
160
        if (empty($this->datePartsAttribute) && $this->dateParts->count() > 1) {
161
            /** @var DatePart $part */
162
            foreach ($this->dateParts as $part) {
163
                $dateParts[] = $part->getName();
164
            }
165
        }
166
167
        /* cs:date may have one or more cs:date-part child elements (see Date-part). The attributes set on
168
        these elements override those specified for the localized date formats (e.g. to get abbreviated months for all
169
        locales, the form attribute on the month-cs:date-part element can be set to “short”). These cs:date-part
170
        elements do not affect which, or in what order, date parts are rendered. Affixes, which are very
171
        locale-specific, are not allowed on these cs:date-part elements. */
172
173
        if ($this->dateParts->count() > 0) {
174
            // ignore empty date-parts
175
            if (!isset($var->{'date-parts'})) {
176
                return "";
177
            }
178
179
            if (count($data->{$this->variable}->{'date-parts'}) === 1) {
180
                $data_ = $this->createDateTime($var->{'date-parts'});
181
                /** @var DatePart $datePart */
182
                foreach ($this->dateParts as $key => $datePart) {
183
                    list($f, $p) = explode("-", $key);
0 ignored issues
show
Unused Code introduced by
The assignment to $f is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
184
                    if (in_array($p, $dateParts)) {
185
                        $ret .= $datePart->render($data_[0], $this);
186
                    }
187
                }
188
            } else if (count($data->{$this->variable}->{'date-parts'}) === 2) { //date range
189
                $data_ = $this->createDateTime($var->{'date-parts'});
190
                $from = $data_[0];
191
                $to = $data_[1];
192
                $interval = $to->diff($from);
193
                $delim = "";
194
                $toRender = 0;
195
                if ($interval->y > 0) {
196
                    $toRender |= self::DATE_RANGE_STATE_YEAR;
197
                    $delim = $this->dateParts->get($this->form . "-year")->getRangeDelimiter();
198
                }
199
                if ($interval->m > 0 && $from->getMonth() - $to->getMonth() !== 0) {
200
                    $toRender |= self::DATE_RANGE_STATE_MONTH;
201
                    $delim = $this->dateParts->get($this->form . "-month")->getRangeDelimiter();
202
                }
203
                if ($interval->d > 0 && $from->getDay() - $to->getDay() !== 0) {
204
                    $toRender |= self::DATE_RANGE_STATE_DAY;
205
                    $delim = $this->dateParts->get($this->form . "-day")->getRangeDelimiter();
206
                }
207
208
                $ret = $this->renderDateRange($toRender, $from, $to, $delim);
209
            } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
210
                // throw
211
            }
212
        }
213
        // fallback:
214
        // When there are no dateParts children, but date-parts attribute in date
215
        // render numeric
216
        else if (!empty($this->datePartsAttribute)) {
217
            $data = $this->createDateTime($var->{'date-parts'});
218
            $ret = $this->renderNumeric($data[0]);
219
        }
220
221
        return !empty($ret) ? $this->addAffixes($this->format($this->applyTextCase($ret))) : "";
222
    }
223
224
    private function renderNumeric(DateTime $date)
225
    {
226
        return $date->renderNumeric();
227
    }
228
229
    public function getForm()
230
    {
231
        return $this->form;
232
    }
233
234
    private function createDateTime($dates)
235
    {
236
        $data = [];
237
        foreach ($dates as $date) {
238
            if ($date[0] < 1000) {
239
                $dateTime = new DateTime(0,0,0);
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a object<DateTimeZone>.

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...
240
                $dateTime->setDay(0)->setMonth(0)->setYear(0);
241
                $data[] = $dateTime;
242
            }
243
            $dateTime = new DateTime($date[0], array_key_exists(1, $date) ? $date[1] : 1, array_key_exists(2, $date) ? $date[2] : 1);
244
            if (!array_key_exists(1, $date)) {
245
                $dateTime->setMonth(0);
246
            }
247
            if (!array_key_exists(2, $date)) {
248
                $dateTime->setDay(0);
249
            }
250
            $data[] = $dateTime;
251
        }
252
253
        return $data;
254
    }
255
256
    /**
257
     * @param $differentParts
258
     * @param DateTime $from
259
     * @param DateTime $to
260
     * @param $delim
261
     * @return string
262
     */
263
    private function renderDateRange($differentParts, DateTime $from, DateTime $to, $delim)
264
    {
265
        $ret = "";
266
        switch ($differentParts) {
267
            case Date::DATE_RANGE_STATE_YEAR:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
268
                foreach ($this->dateParts as $key => $datePart) {
269
                    if (strpos($key, "year") !== false) {
270
                        $ret .= $this->renderOneRangePart($datePart, $from, $to, $delim);
271
                    }
272
                    if (strpos($key, "month") !== false) {
273
                        $day = !empty($d = $from->getMonth()) ? $d : "";
274
                        $ret .= $day;
275
                    }
276 View Code Duplication
                    if (strpos($key, "day") !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
                        $day = !empty($d = $from->getDay()) ? $datePart->render($from, $this) : "";
278
                        $ret .= $day;
279
                    }
280
                }
281
                break;
282
            case Date::DATE_RANGE_STATE_MONTH:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
283
                /**
284
                 * @var string $key
285
                 * @var DatePart $datePart
286
                 */
287
                foreach ($this->dateParts as $key => $datePart) {
288
                    if (strpos($key, "year") !== false) {
289
                        $ret .= $datePart->render($from, $this);
290
                    }
291
                    if (strpos($key, "month")) {
292
                        $ret .= $this->renderOneRangePart($datePart, $from, $to, $delim);
293
                    }
294 View Code Duplication
                    if (strpos($key, "day") !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
295
                        $day = !empty($d = $from->getDay()) ? $datePart->render($from, $this) : "";
296
                        $ret .= $day;
297
                    }
298
                }
299
                break;
300
            case Date::DATE_RANGE_STATE_DAY:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
301
                /**
302
                 * @var string $key
303
                 * @var DatePart $datePart
304
                 */
305
                foreach ($this->dateParts as $key => $datePart) {
306
                    if (strpos($key, "year") !== false) {
307
                        $ret .= $datePart->render($from, $this);
308
                    }
309
                    if (strpos($key, "month") !== false) {
310
                        $ret .= $datePart->render($from, $this);
311
                    }
312
                    if (strpos($key, "day")) {
313
                        $ret .= $this->renderOneRangePart($datePart, $from, $to, $delim);
314
                    }
315
                }
316
                break;
317
            case Date::DATE_RANGE_STATE_YEARMONTHDAY:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
318
                $i = 0;
319 View Code Duplication
                foreach ($this->dateParts as $datePart) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
                    if ($i === $this->dateParts->count() - 1) {
321
                        $ret .= $datePart->renderPrefix();
322
                        $ret .= $datePart->renderWithoutAffixes($from, $this);
323
                    } else {
324
                        $ret .= $datePart->render($from, $this);
325
                    }
326
                    ++$i;
327
                }
328
                $ret .= $delim;
329
                $i = 0;
330 View Code Duplication
                foreach ($this->dateParts as $datePart) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
331
                    if ($i == 0) {
332
                        $ret .= $datePart->renderWithoutAffixes($to, $this);
333
                        $ret .= $datePart->renderSuffix();
334
                    } else {
335
                        $ret .= $datePart->render($to, $this);
336
                    }
337
                    ++$i;
338
                }
339
                break;
340 View Code Duplication
            case Date::DATE_RANGE_STATE_YEARMONTH:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
341
                $dp = $this->dateParts->toArray();
342
                $i = 0;
343
                $dateParts_ = [];
344
                array_walk($dp, function ($datePart, $key) use (&$i, &$dateParts_, $differentParts) {
345
                    if (strpos($key, "year") !== false || strpos($key, "month") !== false) {
346
                        $dateParts_["yearmonth"][] = $datePart;
347
                    }
348
                    if (strpos($key, "day") !== false) {
349
                        $dateParts_["day"] = $datePart;
350
                    }
351
                });
352
                break;
353 View Code Duplication
            case Date::DATE_RANGE_STATE_YEARDAY:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
354
                $dp = $this->dateParts->toArray();
355
                $i = 0;
356
                $dateParts_ = [];
357
                array_walk($dp, function ($datePart, $key) use (&$i, &$dateParts_, $differentParts) {
358
                    if (strpos($key, "year") !== false || strpos($key, "day") !== false) {
359
                        $dateParts_["yearday"][] = $datePart;
360
                    }
361
                    if (strpos($key, "month") !== false) {
362
                        $dateParts_["month"] = $datePart;
363
                    }
364
                });
365
                break;
366 View Code Duplication
            case Date::DATE_RANGE_STATE_MONTHDAY:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
367
                $dp = $this->dateParts->toArray();
368
                $i = 0;
369
                $dateParts_ = [];
370
                array_walk($dp, function ($datePart, $key) use (&$i, &$dateParts_, $differentParts) {
371
                    //$bit = sprintf("%03d", decbin($differentParts));
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
372
                    if (strpos($key, "month") !== false || strpos($key, "day") !== false) {
373
                        $dateParts_["monthday"][] = $datePart;
374
                    }
375
                    if (strpos($key, "year") !== false) {
376
                        $dateParts_["year"] = $datePart;
377
                    }
378
                });
379
                break;
380
        }
381
        switch ($differentParts) {
382
            case Date::DATE_RANGE_STATE_YEARMONTH:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
383
            case Date::DATE_RANGE_STATE_YEARDAY:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
384
            case Date::DATE_RANGE_STATE_MONTHDAY:
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
385
                /**
386
                 * @var $key
387
                 * @var DatePart $datePart */
388
                foreach ($dateParts_ as $key => $datePart) {
0 ignored issues
show
Bug introduced by
The variable $dateParts_ does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
389
                    if (is_array($datePart)) {
390
391
                        $f  = $datePart[0]->render($from, $this);
392
                        $f .= $datePart[1]->renderPrefix();
393
                        $f .= $datePart[1]->renderWithoutAffixes($from, $this);
394
                        $t  = $datePart[0]->renderWithoutAffixes($to, $this);
395
                        $t .= $datePart[0]->renderSuffix();
396
                        $t .= $datePart[1]->render($to, $this);
397
                        $ret .= $f . $delim . $t;
398
                    } else {
399
                        $ret .= $datePart->render($from, $this);
400
                    }
401
                }
402
                break;
403
        }
404
        return $ret;
405
    }
406
407
    /**
408
     * @param $datePart
409
     * @param $from
410
     * @param $to
411
     * @param $delim
412
     * @return string
413
     */
414
    protected function renderOneRangePart(DatePart $datePart, $from, $to, $delim)
415
    {
416
        $prefix = $datePart->renderPrefix();
417
        $from = $datePart->renderWithoutAffixes($from, $this);
418
        $to = $datePart->renderWithoutAffixes($to, $this);
419
        $suffix = !empty($to) ? $datePart->renderSuffix() : "";
420
        return $prefix.$from.$delim.$to.$suffix;
421
    }
422
423
    /**
424
     * @return bool
425
     */
426
    private function hasDatePartsFromLocales($format)
427
    {
428
        $dateXml = CiteProc::getContext()->getLocale()->getDateXml();
429
        return !empty($dateXml[$format]);
430
    }
431
432
    /**
433
     * @return array
434
     */
435
    private function getDatePartsFromLocales($format)
436
    {
437
        $ret = [];
438
        // date parts from locales
439
        $dateFromLocale_ = CiteProc::getContext()->getLocale()->getDateXml();
440
        $dateFromLocale = $dateFromLocale_[$format];
441
442
        // no custom date parts within the date element (this)?
443
        if (!empty($dateFromLocale)) {
444
445
            $dateForm = array_filter(is_array($dateFromLocale) ? $dateFromLocale : [$dateFromLocale], function ($element) use ($format) {
446
                /** @var \SimpleXMLElement $element */
447
                $dateForm = (string)$element->attributes()["form"];
448
                return $dateForm === $format;
449
            });
450
451
            //has dateForm from locale children (date-part elements)?
452
            $localeDate = array_pop($dateForm);
453
454
            if ($localeDate instanceof \SimpleXMLElement && $localeDate->count() > 0) {
455
                foreach ($localeDate as $child) {
456
                    $ret[] = $child;
457
                }
458
            }
459
        }
460
        return $ret;
461
    }
462
463
}