Completed
Push — master ( 5d1976...30add5 )
by mw
13s
created

MonolingualTextValueDescriptionDeserializer.php (1 issue)

strict.coding_against_specific_subtype

Bug Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SMW\Deserializers\DVDescriptionDeserializer;
4
5
use InvalidArgumentException;
6
use SMW\DataValueFactory;
7
use SMW\DataValues\MonolingualTextValue;
8
use SMW\Query\Language\Conjunction;
9
use SMW\Query\Language\SomeProperty;
10
use SMW\Query\Language\ThingDescription;
11
use SMW\Query\Language\ValueDescription;
12
use SMWDIBlob as DIBlob;
13
14
/**
15
 * @private
16
 *
17
 * @license GNU GPL v2+
18
 * @since 2.4
19
 *
20
 * @author mwjames
21
 */
22
class MonolingualTextValueDescriptionDeserializer extends DescriptionDeserializer {
23
24
	/**
25
	 * @since 2.4
26
	 *
27
	 * {@inheritDoc}
28
	 */
29 85
	public function isDeserializerFor( $serialization ) {
30 85
		return $serialization instanceof MonolingualTextValue;
31
	}
32
33
	/**
34
	 * @since 2.4
35
	 *
36
	 * @param string $value
37
	 *
38
	 * @return Description
39
	 * @throws InvalidArgumentException
40
	 */
41 12
	public function deserialize( $value ) {
42
43 12
		if ( !is_string( $value ) ) {
44 1
			throw new InvalidArgumentException( 'Value needs to be a string' );
45
		}
46
47 11
		if ( $value === '' ) {
48 1
			$this->addError( wfMessage( 'smw_novalues' )->text() );
49 1
			return new ThingDescription();
50
		}
51
52 10
		$subdescriptions = array();
53 10
		list( $text, $languageCode ) = $this->dataValue->getValuesFromString( $value );
54
55 10
		foreach ( $this->dataValue->getPropertyDataItems() as $property ) {
56
57
			// If the DVFeature doesn't require a language code to be present then
58
			// allow to skip it as conjunctive condition when it is empty
59
			if (
60 10
				( $languageCode === '' ) &&
61 10
				( $property->getKey() === '_LCODE' ) &&
62 10
				( !$this->dataValue->needsLanguageCode() ) ) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method needsLanguageCode() does only exist in the following sub-classes of SMWDataValue: SMW\DataValues\MonolingualTextValue. 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...
63 2
				continue;
64
			}
65
66 10
			$value = $property->getKey() === '_LCODE' ? $languageCode : $text;
67 10
			$comparator = SMW_CMP_EQ;
68
69 10
			$this->prepareValue( $value, $comparator );
70
71
			// Directly use the DI instead of going through the DVFactory to
72
			// avoid having ~zh-* being validated when building a DV
73
			// If one of the values is empty use, ? so queries can be arbitrary
74
			// in respect of the query condition
75 10
			$dataValue = DataValueFactory::getInstance()->newDataItemValue(
76 10
				new DIBlob( $value === '' ? '?' : $value ),
77
				$property,
78 10
				false,
79 10
				$this->dataValue->getContextPage()
80
			);
81
82 10
			if ( !$dataValue->isValid() ) {
83
				$this->addError( $dataValue->getErrors() );
84
				continue;
85
			}
86
87 10
			$subdescriptions[] = $this->newSubdescription( $dataValue, $comparator );
88
		}
89
90 10
		return $this->getFinalDescriptionFor( $subdescriptions );
91
	}
92
93 10
	private function getFinalDescriptionFor( $subdescriptions ) {
94
95 10
		$count = count( $subdescriptions );
96
97 10
		if ( $count == 0 ) {
98
			return new ThingDescription();
99
		}
100
101 10
		if ( $count == 1 ) {
102 2
			return  reset( $subdescriptions );
103
		}
104
105 8
		return new Conjunction( $subdescriptions );
106
	}
107
108 10
	private function newSubdescription( $dataValue, $comparator ) {
109
110 10
		$description = new ValueDescription(
111 10
			$dataValue->getDataItem(),
112 10
			$dataValue->getProperty(),
113
			$comparator
114
		);
115
116 10
		if ( $dataValue->getWikiValue() === '+' || $dataValue->getWikiValue() === '?' ) {
117 7
			$description = new ThingDescription();
118
		}
119
120 10
		return new SomeProperty(
121 10
			$dataValue->getProperty(),
122
			$description
123
		);
124
	}
125
126
}
127