Passed
Push — issue-71 ( 957702 )
by Sebastian
06:11
created

NumberHelper::isRange()   A

Complexity

Conditions 6
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 8
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 13
ccs 9
cts 9
cp 1
crap 6
rs 9.2222
1
<?php
2
/*
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
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\Util;
11
12
use Closure;
13
14
/**
15
 * Class Number
16
 * @package Seboettg\CiteProc\Util
1 ignored issue
show
Coding Style introduced by
There must be exactly one blank line before the tags in a doc comment
Loading history...
17
 *
18
 * @author Sebastian Böttger <[email protected]>
19
 */
3 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
20
class NumberHelper
21
{
22
23
    const PATTERN_ORDINAL = "/\d+(st|nd|rd|th)?\.?$/";
24
25
    const PATTERN_ROMAN = "/^[ivxlcdm]+\.?$/i";
26
27
    const PATTERN_AFFIXES = "/^[a-z]?\d+[a-z]?$/i";
28
29
    const PATTERN_COMMA_AMPERSAND_RANGE = "/\d*([\s?\-&+,;\s])+\d+/";
30
31
    const ROMAN_NUMERALS = [
32
        ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"],
33
        ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"],
34
        ["", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"],
35
        ["", "m", "mm", "mmm", "mmmm", "mmmmm"]
36
    ];
37
38
    const ROMAN_DIGITS = [
39
        "M" => 1000,
40
        "D" => 500,
41
        "C" => 100,
42
        "L" => 50,
43
        "X" => 10,
44
        "V" => 5,
45
        "I" => 1
46
    ];
47
48
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
49
     * @return Closure
50
     */
51
    public static function getCompareNumber()
52
    {
53
        return function($numA, $numB, $order) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
54
            if (is_numeric($numA) && is_numeric($numB)) {
55
                $ret = $numA - $numB;
56
            } else {
57
                $ret = strcasecmp($numA, $numB);
58
            }
59
            if ("descending" === $order) {
60
                return $ret > 0 ? -1 : 1;
61
            }
62
            return $ret > 0 ? 1 : -1;
63
        };
64
    }
65
66
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
67
     * @param $num
2 ignored issues
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
68
     * @return string
1 ignored issue
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
69
     */
70 4
    public static function dec2roman($num)
71
    {
72 4
        $ret = "";
73 4
        if ($num < 6000) {
74
75 4
            $numStr = strrev($num);
76 4
            $len = strlen($numStr);
77 4
            for ($pos = 0; $pos < $len; $pos++) {
78 4
                $n = $numStr[$pos];
79 4
                $ret = self::ROMAN_NUMERALS[$pos][$n] . $ret;
80
            }
81
        }
82 4
        return $ret;
83
    }
84
85
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
86
     * @param $romanNumber
2 ignored issues
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
87
     * @return int|mixed
1 ignored issue
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
88
     */
89 4
    public static function roman2Dec($romanNumber)
90
    {
91 4
        $romanNumber = trim($romanNumber);
92 4
        if (is_numeric($romanNumber)) {
93
            return 0;
94
        }
95
96 4
        $values = [];
97
        // Convert the string to an array of roman values:
98 4
        for ($i = 0; $i < strlen($romanNumber); ++$i) {
99 4
            $char = strtoupper($romanNumber{$i});
100 4
            if (self::ROMAN_DIGITS[$char] !== null) {
101 4
                $values[] = self::ROMAN_DIGITS[$char];
102
            }
103
        }
104
105 4
        $sum = 0;
106 4
        while ($current = current($values)) {
107 4
            $next = next($values);
108 4
            $next > $current ? $sum += $next - $current + 0 * next($values) : $sum += $current;
109
        }
110 4
        return $sum;
111
    }
112
113 22
    public static function isRomanNumber($str)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function isRomanNumber()
Loading history...
114
    {
115 22
        $number = trim($str);
116 22
        for ($i = 0; $i < strlen($number); ++$i) {
117 22
            $char = strtoupper($number{$i});
118 22
            if (!in_array($char, array_keys(self::ROMAN_DIGITS))) {
119 21
                return false;
120
            }
121
        }
122 4
        return true;
123
    }
124
125
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
126
     * @param $str
2 ignored issues
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
127
     * @return string
1 ignored issue
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
128
     */
129 1
    public static function evaluateStringPluralism($str)
130
    {
131 1
        $plural = 'single';
132 1
        if (!empty($str)) {
133 1
            $isRange = self::isRange($str);
134 1
            if ($isRange) {
135 1
                return 'multiple';
136
            } else {
137 1
                if (is_numeric($str) || NumberHelper::isRomanNumber($str)) {
138 1
                    return 'single';
139
                }
140
            }
141
        }
142
        return $plural;
143
    }
144
145 49
    public static function extractNumber($string)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function extractNumber()
Loading history...
146
    {
147 49
        if (preg_match("/(\d+)[^\d]*$/", $string, $match)) {
148 49
            return $match[1];
149
        }
150 3
        return $string;
151
    }
152
153
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
154
     * @param $str
2 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
155
     * @return array[]|false|string[]
1 ignored issue
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
156
     */
157 20
    public static function splitByRangeDelimiter($str)
158
    {
159 20
        return preg_split("/[-–&,]/", $str);
160
    }
161
162
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
163
     * @param string $str
2 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
164
     * @return bool
1 ignored issue
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
165
     */
166 1
    private static function isRange($str)
1 ignored issue
show
Coding Style introduced by
Private method name "NumberHelper::isRange" must be prefixed with an underscore
Loading history...
167
    {
168 1
        $rangeParts = self::splitByRangeDelimiter($str);
169 1
        $isRange = false;
170 1
        if (count($rangeParts) > 1) {
0 ignored issues
show
Bug introduced by
It seems like $rangeParts can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
        if (count(/** @scrutinizer ignore-type */ $rangeParts) > 1) {
Loading history...
171 1
            $isRange = true;
172 1
            foreach ($rangeParts as $range) {
173 1
                if (NumberHelper::isRomanNumber(trim($range)) || is_numeric(trim($range))) {
174 1
                    $isRange = $isRange && true;
175
                }
176
            }
177
        }
178 1
        return $isRange;
179
    }
180
181
    /**
1 ignored issue
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
182
     * @param int|string $number
2 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
183
     * @return bool
1 ignored issue
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
184
     */
185 19
    public static function isRomanRange($number)
186
    {
187 19
        $rangeParts = array_map("trim", self::splitByRangeDelimiter($number));
0 ignored issues
show
Bug introduced by
It seems like self::splitByRangeDelimiter($number) can also be of type false; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

187
        $rangeParts = array_map("trim", /** @scrutinizer ignore-type */ self::splitByRangeDelimiter($number));
Loading history...
188 19
        $isRange = false;
189 19
        if (count($rangeParts) > 1) {
190 11
            $isRange = true;
191 11
            foreach ($rangeParts as $part) {
192 11
                $isRange = $isRange && NumberHelper::isRomanNumber($part);
193
            }
194
        }
195 19
        return $isRange;
196
    }
197
}
198