Completed
Pull Request — master (#2037)
by mw
123:18 queued 87:36
created

ResultFieldMatchFinder::getResultsBy()   C

Complexity

Conditions 10
Paths 11

Size

Total Lines 57
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 10

Importance

Changes 0
Metric Value
cc 10
eloc 28
nc 11
nop 1
dl 0
loc 57
ccs 25
cts 25
cp 1
crap 10
rs 6.7123
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
namespace SMW\Query\Result;
4
5
use SMW\Query\PrintRequest;
6
use SMW\DataValueFactory;
7
use SMW\RequestOptions;
8
use SMW\DIProperty;
9
use SMW\DIWikiPage;
10
use SMW\Store;
11
use SMWDataItem as DataItem;
12
use SMWDIBoolean as DIBoolean;
13
use SMW\DataValues\MonolingualTextValue;
14
15
/**
16
 * Returns the result content (DI objects) for a single PrintRequest represented
17
 * as cell of the intersection between a subject row and a print column.
18
 *
19
 * @license GNU GPL v2+
20
 * @since 2.5
21
 *
22
 * @author Markus Krötzsch
23
 * @author Jeroen De Dauw < [email protected] >
24
 * @author mwjames
25
 */
26
class ResultFieldMatchFinder {
27
28
	/**
29
	 * @var Store
30
	 */
31
	private $store;
32
33
	/**
34
	 * @var PrintRequest
35
	 */
36
	private $printRequest;
37
38
	/**
39
	 * @var boolean|array
40
	 */
41
	private static $catCacheObj = false;
42
43
	/**
44
	 * @var boolean|array
45
	 */
46
	private static $catCache = false;
47
48
	/**
49
	 * @since 2.5
50
	 *
51
	 * @param Store $store
52
	 * @param PrintRequest $printRequest
53 102
	 */
54 102
	public function __construct( Store $store, PrintRequest $printRequest ) {
55 102
		$this->printRequest = $printRequest;
56 102
		$this->store = $store;
57
	}
58
59
	/**
60
	 * @since 2.5
61
	 *
62
	 * @param DataItem $dataItem
63
	 *
64
	 * @param DataItem[]|[]
65 100
	 */
66
	public function getResultsBy( DataItem $dataItem ) {
67 100
68
		$content = array();
69
70
		// Request the current element (page in result set).
71 100
		// The limit is ignored here.
72 68
		if ( $this->printRequest->isMode( PrintRequest::PRINT_THIS ) ) {
73
			return array( $dataItem );
74
		}
75
76
		// Request all direct categories of the current element
77 91
		// Always recompute cache here to ensure output format is respected.
78 4
		if ( $this->printRequest->isMode( PrintRequest::PRINT_CATS ) ) {
79
			self::$catCache = $this->store->getPropertyValues(
80 4
				$dataItem,
81 4
				new DIProperty( '_INST' ),
82
				$this->getRequestOptions( false )
83
			);
84 4
85
			self::$catCacheObj = $dataItem->getHash();
0 ignored issues
show
Documentation Bug introduced by
It seems like $dataItem->getHash() of type string is incompatible with the declared type boolean|array of property $catCacheObj.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
86 4
87
			$limit = $this->printRequest->getParameter( 'limit' );
88 4
89
			return ( $limit === false ) ? ( self::$catCache ) : array_slice( self::$catCache, 0, $limit );
90
		}
91
92
		// Request to whether current element is in given category (Boolean printout).
93 90
		// The limit is ignored here.
94 1
		if ( $this->printRequest->isMode( PrintRequest::PRINT_CCAT ) ) {
95 1
			if ( self::$catCacheObj !== $dataItem->getHash() ) {
96
				self::$catCache = $this->store->getPropertyValues(
97 1
					$dataItem,
98
					new DIProperty( '_INST' )
99 1
				);
100
				self::$catCacheObj = $dataItem->getHash();
101
			}
102 1
103 1
			$found = false;
104
			$prkey = $this->printRequest->getData()->getDBkey();
105 1
106 1
			foreach ( self::$catCache as $cat ) {
0 ignored issues
show
Bug introduced by
The expression self::$catCache of type boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
107 1
				if ( $cat->getDBkey() == $prkey ) {
108 1
					$found = true;
109
					break;
110
				}
111
			}
112 1
113
			return array( new DIBoolean( $found ) );
114
		}
115
116 89
		// Request all property values of a certain attribute of the current element.
117
		if ( $this->printRequest->isMode( PrintRequest::PRINT_PROP ) || $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
118 89
			return $this->getResultsForProperty( $dataItem );
119
		}
120
121
		return $content;
122 89
	}
123 88
124
	/**
125
	 * Make a request option object based on the given parameters, and
126
	 * return NULL if no such object is required. The parameter defines
127
	 * if the limit should be taken into account, which is not always desired
128
	 * (especially if results are to be cached for future use).
129
	 *
130 5
	 * @param boolean $useLimit
131
	 *
132
	 * @return RequestOptions|null
133 5
	 */
134
	public function getRequestOptions( $useLimit = true ) {
135
		$limit = $useLimit ? $this->printRequest->getParameter( 'limit' ) : false;
136 5
		$order = trim( $this->printRequest->getParameter( 'order' ) );
137 5
		$options = null;
138
139 5
		// Important: use "!=" for order, since trim() above does never return "false", use "!==" for limit since "0" is meaningful here.
140
		if ( ( $limit !== false ) || ( $order != false ) ) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $order of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
141
			$options = new RequestOptions();
142 5
143
			if ( $limit !== false ) {
144 5
				$options->limit = trim( $limit );
0 ignored issues
show
Documentation Bug introduced by
The property $limit was declared of type integer, but trim($limit) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
145
			}
146
147 5
			if ( ( $order == 'descending' ) || ( $order == 'reverse' ) || ( $order == 'desc' ) ) {
148 5
				$options->sort = true;
149
				$options->ascending = false;
150
			} elseif ( ( $order == 'ascending' ) || ( $order == 'asc' ) ) {
151
				$options->sort = true;
152 5
				$options->ascending = true;
153 5
			}
154
		}
155
156 5
		return $options;
157
	}
158
159
	private function getResultsForProperty( $dataItem ) {
160
161
		$content = $this->getResultContent(
162
			$dataItem
163
		);
164
165
		if ( !$this->isMultiValueWithParameter( 'index' ) && !$this->isMultiValueWithParameter( 'lang' ) ) {
166
			return $content;
167
		}
168
169 91
		// Print one component of a multi-valued string.
170 91
		//
171 91
		// Known limitation: the printrequest still is of type _rec, so if
172 91
		// printers check for this then they will not recognize that it returns
173
		// some more concrete type.
174
		if ( $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
175 91
			$propertyValue = $this->printRequest->getData()->getLastPropertyChainValue();
176 4
		} else {
177
			$propertyValue = $this->printRequest->getData();
178 4
		}
179 3
180
		$index = $this->printRequest->getParameter( 'index' );
181
		$lang = $this->printRequest->getParameter( 'lang' );
182 4
		$newcontent = array();
183 2
184 2
		// Replace content with specific content from a Container/MultiValue
185 3
		foreach ( $content as $diContainer ) {
186 1
187 1
			/* AbstractMultiValue */
188
			$multiValue = DataValueFactory::getInstance()->newDataValueByItem(
189
				$diContainer,
190
				$propertyValue->getDataItem()
191 91
			);
192
193
			if ( $multiValue instanceof MonolingualTextValue && $lang !== false && ( $textValue = $multiValue->getTextValueByLanguage( $lang ) ) !== null ) {
194 89
195 89
				// Return the text representation without a language reference
196
				// (tag) since the value has been filtered hence only matches
197
				// that language
198 89
				$newcontent[] = $textValue->getDataItem();
199
200 89
				// Set the index so ResultArray::getNextDataValue can
201 89
				// find the correct PropertyDataItem (_TEXT;_LCODE) position
202
				// to match the DI
203 89
				$this->printRequest->setParameter( 'index', 1 );
204
			} elseif ( ( $dataItemByRecord = $multiValue->getDataItemByIndex( $index ) ) !== null ) {
0 ignored issues
show
Bug introduced by
The method getDataItemByIndex() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
205
				$newcontent[] = $dataItemByRecord;
206
			}
207
		}
208
209
		$content = $newcontent;
210
		unset( $newcontent );
211
212
		return $content;
213
	}
214
215 89
	private function isMultiValueWithParameter( $parameter ) {
216
		return strpos( $this->printRequest->getTypeID(), '_rec' ) !== false && $this->printRequest->getParameter( $parameter ) !== false;
217
	}
218 1
219 1
	private function getResultContent( DataItem $dataItem ) {
220
221
		$dataValue = $this->printRequest->getData();
222
		$dataItems = array( $dataItem );
223 1
224 1
		if ( !$dataValue->isValid() ) {
225
			return array();
226
		}
227
228 1
		// If it is a chain then try to find a connected DIWikiPage subject that
229
		// matches the property on the chained PrintRequest.
230
		// For example, Number.Date.SomeThing will not return any meaningful results
231 89
		// because Number will return a DINumber object and not a DIWikiPage.
232
		// If on the other hand Has page.Number (with Number being the Last and
233
		// `Has page` is of type Page) then the iteration will lookup on results
234 89
		// for `Has page` and try to match a Number annotation on the results
235
		// retrieved from `Has page`.
236 89
		if ( $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
237
238 89
			// Output of the previous iteration is the input for the next iteration
239
			foreach ( $dataValue->getPropertyChainValues() as $pv ) {
240 89
				$dataItems = $this->doFetchPropertyValues( $dataItems, $pv );
241
242
				// If the results return empty then it means that for this element
243
				// the chain has no matchable items hence we stop
244 89
				if ( $dataItems === array() ) {
245
					return array();
246 89
				}
247 89
			}
248
249
			$dataValue = $dataValue->getLastPropertyChainValue();
250 89
		}
251 89
252
		return $this->doFetchPropertyValues( $dataItems, $dataValue );
253
	}
254 89
255
	private function doFetchPropertyValues( $dataItems, $dataValue ) {
256
257
		$propertyValues = array();
258
259
		foreach ( $dataItems as $dataItem ) {
260
261
			if ( !$dataItem instanceof DIWikiPage ) {
262
				continue;
263
			}
264
265
			$pv = $this->store->getPropertyValues(
266
				$dataItem,
267
				$dataValue->getDataItem(),
268
				$this->getRequestOptions()
269
			);
270
271
			$propertyValues = array_merge( $propertyValues, $pv );
272
			unset( $pv );
273
		}
274
275
		return $propertyValues;
276
	}
277
278
}
279