Completed
Push — master ( cd2f8d...f0067b )
by mw
37:19 queued 02:22
created

TemperatureValue::getUnitID()   C

Complexity

Conditions 14
Paths 14

Size

Total Lines 26
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 26
rs 5.0864
c 1
b 0
f 0
cc 14
eloc 21
nc 14
nop 1

How to fix   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
namespace SMW\DataValues;
4
5
use SMW\NumberFormatter;
6
use SMWNumberValue as NumberValue;
7
use SMWDINumber as DINumber;
8
9
/**
10
 * This datavalue implements unit support for measuring temperatures. This is
11
 * mostly an example implementation of how to realise custom unit types easily.
12
 *
13
 * @license GNU GPL v2+
14
 * @since 2.4
15
 *
16
 * @author Markus Krötzsch
17
 * @author mwjames
18
 */
19
class TemperatureValue extends NumberValue {
20
21
	/**
22
	 * @param string $typeid
23
	 */
24
	public function __construct( $typeid = '' ) {
25
		parent::__construct( '_tem' );
26
	}
27
28
	/**
29
	 * NumberValue::convertToMainUnit
30
	 */
31
	protected function convertToMainUnit( $number, $unit ) {
32
33
		$this->m_unitin = $this->getUnitID( $unit );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getUnitID($unit) can also be of type false. However, the property $m_unitin is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
34
35
		if ( ( $value = $this->convertToKelvin( $number, $this->m_unitin ) ) === false ) {
36
			return false;
37
		}
38
39
		$this->m_dataitem = new DINumber( $value );
40
41
		return true;
42
	}
43
44
	/**
45
	 * NumberValue::makeConversionValues
46
	 */
47
	protected function makeConversionValues() {
48
49
		if ( $this->m_unitvalues !== false ) {
50
			return; // do this only once
51
		}
52
53
		$this->m_unitvalues = array();
54
55
		if ( !$this->isValid() ) {
56
			return $this->m_unitvalues;
57
		}
58
59
		$displayUnit = $this->getPreferredDisplayUnit();
60
		$number = $this->m_dataitem->getNumber();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getNumber() does only exist in the following sub-classes of SMWDataItem: SMWDINumber. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
61
62
		$unitvalues = array(
63
			'K'  => $number,
64
			'°C' => $number - 273.15,
65
			'°F' => ( $number - 273.15 ) * 1.8 + 32,
66
			'°R' => ( $number ) * 1.8
67
		);
68
69
		if ( isset( $unitvalues[$displayUnit] ) ) {
70
			$this->m_unitvalues[$displayUnit] = $unitvalues[$displayUnit];
71
		}
72
73
		$this->m_unitvalues += $unitvalues;
74
	}
75
76
	/**
77
	 * NumberValue::makeUserValue
78
	 */
79
	protected function makeUserValue() {
80
81
		$printunit = '';
0 ignored issues
show
Unused Code introduced by
$printunit 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...
82
83
		if ( ( $this->m_outformat ) && ( $this->m_outformat != '-' ) &&
84
		     ( $this->m_outformat != '-n' ) && ( $this->m_outformat != '-u' ) ) { // first try given output unit
85
			$printunit = NumberValue::normalizeUnit( $this->m_outformat );
86
			$this->m_unitin = $this->getUnitID( $printunit );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getUnitID($printunit) can also be of type false. However, the property $m_unitin is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
87
		} else {
88
			$this->m_unitin = $this->getPreferredDisplayUnit();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getPreferredDisplayUnit() can also be of type false. However, the property $m_unitin is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
89
			$printunit = $this->m_unitin;
90
		}
91
92
		$value =$this->convertToUnit(
93
			$this->m_dataitem->getNumber(),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getNumber() does only exist in the following sub-classes of SMWDataItem: SMWDINumber. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
94
			$this->m_unitin
95
		);
96
97
		// -u is the format for displaying the unit only
98
		if ( $this->m_outformat == '-u' ) {
99
			$this->m_caption = '';
100
		} elseif ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ) {
101
			$this->m_caption = $this->getLocalizedFormattedNumberByPrecision( $value );
102
			$this->m_caption .= '&#160;';
103
		} else {
104
			$this->m_caption = $this->getUnformattedNumberByPrecision( $value );
105
			$this->m_caption .=  ' ';
106
		}
107
108
		// -n is the format for displaying the number only
109
		if ( $this->m_outformat == '-n' ) {
110
			$printunit = '';
111
		}
112
113
		$this->m_caption .= $printunit;
114
	}
115
116
	/**
117
	 * Helper method to find the main representation of a certain unit.
118
	 */
119
	protected function getUnitID( $unit ) {
120
		/// TODO possibly localise some of those strings
121
		switch ( $unit ) {
122
			case '':
123
			case 'K':
124
			case 'Kelvin':
125
			case 'kelvin':
126
			case 'kelvins':
127
			return 'K';
128
			// There's a dedicated Unicode character (℃, U+2103) for degrees C.
129
			// Your font may or may not display it; do not be alarmed.
130
			case '°C':
131
			case '℃':
132
			case 'Celsius':
133
			case 'centigrade':
134
			return '°C';
135
			case '°F':
136
			case 'Fahrenheit':
137
			return '°F';
138
			case '°R':
139
			case 'Rankine':
140
			return '°R';
141
			default:
142
			return false;
143
		}
144
	}
145
146
	/**
147
	 * NumberValue::getUnitList
148
	 */
149
	public function getUnitList() {
150
		return array( 'K', '°C', '°F', '°R' );
151
	}
152
153
	/**
154
	 * NumberValue::getUnit
155
	 */
156
	public function getUnit() {
157
		return 'K';
158
	}
159
160
	private function getPreferredDisplayUnit() {
161
162
		$unit = $this->getUnit();
163
164
		if ( $this->getProperty() === null ) {
165
			return $unit;
166
		}
167
168
		$units = $this->getPropertySpecificationLookup()->getDisplayUnitsFor(
169
			$this->getProperty()
170
		);
171
172
		if ( $units !== null && $units !== array() ) {
173
			$unit = $this->getUnitID( end( $units ) );
174
		}
175
176
		return $this->getUnitID( $unit );
177
	}
178
179
	private function convertToKelvin( $number, $unit ) {
180
181
		switch ( $unit ) {
182
			case 'K':
183
				return $number;
184
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
185
			case '°C':
186
				return $number + 273.15;
187
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
188
			case '°F':
189
				return ( $number - 32 ) / 1.8 + 273.15;
190
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
191
			case '°R':
192
				return ( $number ) / 1.8;
193
		}
194
195
		return false; // unsupported unit
196
	}
197
198
	private function convertToUnit( $number, $unit ) {
199
200
		switch ( $unit ) {
201
			case 'K':
202
				return $number;
203
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
204
			case '°C':
205
				return $number - 273.15;
206
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
207
			case '°F':
208
				return ( $number - 273.15 ) * 1.8 + 32;
209
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
210
			case '°R':
211
				return ( $number ) * 1.8;
212
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
213
			// default: unit not supported
214
		}
215
216
		return 0;
217
	}
218
219
}
220