SRFTimeline   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 410
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 78.21%

Importance

Changes 0
Metric Value
wmc 63
lcom 1
cbo 4
dl 0
loc 410
ccs 140
cts 179
cp 0.7821
rs 3.36
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A handleParameters() 0 14 1
A getName() 0 5 1
C getResultText() 0 48 14
F handlePropertyValue() 0 94 24
A getParamDefinitions() 0 44 1
F getEventsHTML() 0 137 22

How to fix   Complexity   

Complex Class

Complex classes like SRFTimeline often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SRFTimeline, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Print query results in interactive timelines.
4
 *
5
 * @file SRF_Timeline.php
6
 * @ingroup SemanticResultFormats
7
 *
8
 * @author Markus Krötzsch
9
 *
10
 * FIXME: this code is just insane; rewrite from 0 is probably the only way to get it right
11
 */
12
13
/**
14
 * Result printer for timeline data.
15
 *
16
 * @ingroup SemanticResultFormats
17
 */
18
class SRFTimeline extends SMWResultPrinter {
19
20
	protected $m_tlstart = '';  // name of the start-date property if any
21
	protected $m_tlend = '';  // name of the end-date property if any
22
	protected $m_tlsize = ''; // CSS-compatible size (such as 400px)
23
	protected $m_tlbands = ''; // array of band IDs (MONTH, YEAR, ...)
24
	protected $m_tlpos = ''; // position identifier (start, end, today, middle)
25
	protected $mTemplate;
26
	protected $mNamedArgs;
27
28
	/**
29
	 * @see SMWResultPrinter::handleParameters
30
	 *
31
	 * @since 1.6.3
32
	 *
33
	 * @param array $params
34
	 * @param $outputmode
35
	 */
36 1
	protected function handleParameters( array $params, $outputmode ) {
37 1
		parent::handleParameters( $params, $outputmode );
38
39 1
		$this->mTemplate = trim( $params['template'] );
40 1
		$this->mNamedArgs = $params['named args'];
41 1
		$this->m_tlstart = smwfNormalTitleDBKey( $params['timelinestart'] );
42 1
		$this->m_tlend = smwfNormalTitleDBKey( $params['timelineend'] );
43 1
		$this->m_tlbands = $params['timelinebands'];
44 1
		$this->m_tlpos = strtolower( trim( $params['timelineposition'] ) );
45
46
		// str_replace makes sure this is only one value, not mutliple CSS fields (prevent CSS attacks)
47
		// / FIXME: this is either unsafe or redundant, since Timeline is Wiki-compatible. If the JavaScript makes user inputs to CSS then it is bad even if we block this injection path.
48 1
		$this->m_tlsize = htmlspecialchars( str_replace( ';', ' ', strtolower( $params['timelinesize'] ) ) );
49 1
	}
50
51
	public function getName() {
52
		// Give grep a chance to find the usages:
53
		// srf_printername_timeline, srf_printername_eventline
54
		return wfMessage( 'srf_printername_' . $this->mFormat )->text();
55
	}
56
57 1
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
58
59 1
		SMWOutputs::requireHeadItem( SMW_HEADER_STYLE );
60 1
		SMWOutputs::requireResource( 'ext.srf.timeline' );
61
62 1
		$isEventline = 'eventline' == $this->mFormat;
63 1
		$id = uniqid();
64
65 1
		if ( !$isEventline && ( $this->m_tlstart == '' ) ) { // seek defaults
66 1
			foreach ( $res->getPrintRequests() as $pr ) {
67 1
				if ( ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) && ( $pr->getTypeID() == '_dat' ) ) {
68 1
					$dataValue = $pr->getData();
69
70 1
					$date_value = $dataValue->getDataItem()->getLabel();
71
72 1
					if ( ( $this->m_tlend == '' ) && ( $this->m_tlstart != '' ) &&
73 1
						( $this->m_tlstart != $date_value ) ) {
74
						$this->m_tlend = $date_value;
75 1
					} elseif ( ( $this->m_tlstart == '' ) && ( $this->m_tlend != $date_value ) ) {
76 1
						$this->m_tlstart = $date_value;
77
					}
78
				}
79
			}
80
		}
81
82
		// print header
83 1
		$result = "<div id=\"smwtimeline-$id\" class=\"smwtimeline is-disabled\" style=\"height: $this->m_tlsize\">";
84 1
		$result .= '<span class="smw-overlay-spinner medium" style="top:40%; transform: translate(-50%, -50%);"></span>';
85
86 1
		foreach ( $this->m_tlbands as $band ) {
0 ignored issues
show
Bug introduced by Jeroen De Dauw
The expression $this->m_tlbands of type string is not traversable.
Loading history...
87 1
			$result .= '<span class="smwtlband" style="display:none;">' . htmlspecialchars( $band ) . '</span>';
88
			// just print any "band" given, the JavaScript will figure out what to make of it
89
		}
90
91
		// print all result rows
92 1
		if ( ( $this->m_tlstart != '' ) || $isEventline ) {
93 1
			$result .= $this->getEventsHTML( $res, $outputmode, $isEventline );
94
		}
95
		// no further results displayed ...
96
97
		// print footer
98 1
		$result .= '</div>';
99
100
		// yes, our code can be viewed as HTML if requested, no more parsing needed
101 1
		$this->isHTML = $outputmode == SMW_OUTPUT_HTML;
102
103 1
		return $result;
104
	}
105
106
	/**
107
	 * Returns the HTML for the events.
108
	 *
109
	 * @since 1.5.3
110
	 *
111
	 * @param SMWQueryResult $res
112
	 * @param $outputmode
113
	 * @param boolean $isEventline
114
	 *
115
	 * @return string
116
	 */
117 1
	protected function getEventsHTML( SMWQueryResult $res, $outputmode, $isEventline ) {
118 1
		global $curarticle, $cururl; // why not, code flow has reached max insanity already
119
120 1
		$positions = []; // possible positions, collected to select one for centering
121 1
		$curcolor = 0; // color cycling is used for eventline
122
123 1
		$result = '';
124
125 1
		$output = false; // true if output for the popup was given on current line
126 1
		if ( $isEventline ) {
127
			$events = [];
128
		} // array of events that are to be printed
129
130 1
		while ( $row = $res->getNext() ) { // Loop over the objcts (pages)
131 1
			$hastime = false; // true as soon as some startdate value was found
132 1
			$hastitle = false; // true as soon as some label for the event was found
133 1
			$curdata = ''; // current *inner* print data (within some event span)
134 1
			$curmeta = ''; // current event meta data
135 1
			$cururl = '';
136 1
			$curarticle = ''; // label of current article, if it was found; needed only for eventline labeling
137 1
			$first_col = true;
138
139 1
			if ( $this->mTemplate != '' ) {
140
				$this->hasTemplates = true;
141
				$template_text = '';
142
				$i = 0;
143
			}
144
145 1
			foreach ( $row as $field ) { // Loop over the returned properties
146 1
				$first_value = true;
147 1
				$pr = $field->getPrintRequest();
148 1
				$dataValue = $pr->getData();
149
150 1
				if ( $dataValue == '' ) {
151 1
					$date_value = null;
152
				} else {
153 1
					$date_value = $dataValue->getDataItem()->getLabel();
154
				}
155
156 1
				while ( ( $object = $field->getNextDataValue() ) !== false ) { // Loop over property values
157 1
					$event = $this->handlePropertyValue(
158 1
						$object,
159
						$outputmode,
160
						$pr,
161
						$first_col,
162
						$hastitle,
163
						$hastime,
164
						$first_value,
165
						$isEventline,
166
						$curmeta,
167
						$curdata,
168
						$date_value,
169
						$output,
170
						$positions
171
					);
172
173 1
					if ( $this->mTemplate != '' ) {
174
						$template_text .= '|' . ( $this->mNamedArgs ? '?' . $field->getPrintRequest()->getLabel(
0 ignored issues
show
Bug introduced by Jeroen De Dauw
The variable $template_text 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...
175
								) : $i + 1 ) . '=';
0 ignored issues
show
Bug introduced by Jeroen De Dauw
The variable $i 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...
176
						if ( !$first_value ) {
177
							$template_text .= ', ';
178
						}
179
						$template_text .= $object->getShortText( SMW_OUTPUT_WIKI, $this->getLinker( $first_value ) );
180
						$i++;
181
					}
182
183 1
					if ( $event !== false ) {
184
						$events[] = $event;
0 ignored issues
show
Bug introduced by Jeroen De Dauw
The variable $events 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...
185
					}
186
187 1
					$first_value = false;
188
				}
189
190 1
				if ( $output ) {
191 1
					$curdata .= '<br />';
192
				}
193
194 1
				$output = false;
195 1
				$first_col = false;
196
			}
197
198 1
			if ( $this->mTemplate != '' ) {
199
				$curdata = '{{' . $this->mTemplate . $template_text . '}}';
200
			}
201
202 1
			if ( $hastime ) {
203 1
				$result .= Html::rawElement(
204 1
					'span',
205 1
					[ 'class' => 'smwtlevent', 'style' => 'display:none;' ],
206 1
					$curmeta . Html::element(
207 1
						'span',
208 1
						[ 'class' => 'smwtlcoloricon' ],
209 1
						$curcolor
210 1
					) . $curdata
211
				);
212
			}
213
214 1
			if ( $isEventline ) {
215
				foreach ( $events as $event ) {
216
					$result .= '<span class="smwtlevent" style="display:none;" ><span class="smwtlstart">' . $event[0] . '</span><span class="smwtlurl">' . $curarticle . '</span><span class="smwtlcoloricon">' . $curcolor . '</span>';
217
					if ( $curarticle != '' ) {
218
						$result .= '<span class="smwtlprefix">' . $curarticle . ' </span>';
219
					}
220
					$result .= $curdata . '</span>';
221
					$positions[$event[2]] = $event[0];
222
				}
223
				$events = [];
224
				$curcolor = ( $curcolor + 1 ) % 10;
225
			}
226
		}
227
228 1
		if ( count( $positions ) > 0 ) {
229 1
			ksort( $positions );
230 1
			$positions = array_values( $positions );
231
232 1
			switch ( $this->m_tlpos ) {
233 1
				case 'start':
234
					$result .= '<span class="smwtlposition" style="display:none;" >' . $positions[0] . '</span>';
235
					break;
236 1
				case 'end':
237
					$result .= '<span class="smwtlposition" style="display:none;" >' . $positions[count(
238
							$positions
239
						) - 1] . '</span>';
240
					break;
241 1
				case 'today':
242
					break; // default
243 1
				case 'middle':
244
				default:
245 1
					$result .= '<span class="smwtlposition" style="display:none;" >' . $positions[ceil(
246 1
							count( $positions ) / 2
247 1
						) - 1] . '</span>';
248 1
					break;
249
			}
250
		}
251
252 1
		return $result;
253
	}
254
255
	/**
256
	 * Hanldes a single property value. Returns an array with data for a single event or false.
257
	 *
258
	 * FIXME: 13 arguments, of which a whole bunch are byref... not a good design :)
259
	 *
260
	 * @since 1.5.3
261
	 *
262
	 * @param SMWDataValue $object
263
	 * @param $outputmode
264
	 * @param SMWPrintRequest $pr
265
	 * @param boolean $first_col
266
	 * @param boolean &$hastitle
267
	 * @param boolean &$hastime
268
	 * @param boolean $first_value
269
	 * @param boolean $isEventline
270
	 * @param string &$curmeta
271
	 * @param string &$curdata
272
	 * @param &$date_value
273
	 * @param boolean &$output
274
	 * @param array &$positions
275
	 *
276
	 * @return false or array
277
	 */
278 1
	protected function handlePropertyValue( SMWDataValue $object, $outputmode, SMWPrintRequest $pr, $first_col,
279
		&$hastitle, &$hastime, $first_value, $isEventline, &$curmeta, &$curdata, $date_value, &$output, array &$positions ) {
280 1
		global $curarticle, $cururl;
281
282 1
		$event = false;
283
284 1
		$l = $this->getLinker( $first_col );
285
286 1
		if ( !$hastitle && $object->getTypeID(
287 1
			) != '_wpg' ) { // "linking" non-pages in title positions confuses timeline scripts, don't try this
288
			$l = null;
289
		}
290
291 1
		if ( $object->getTypeID() == '_wpg' ) { // use shorter "LongText" for wikipage
292 1
			$objectlabel = $object->getLongText( $outputmode, $l );
293
		} else {
294 1
			$objectlabel = $object->getShortText( $outputmode, $l );
295
		}
296
297 1
		$urlobject = ( $l !== null );
298 1
		$header = '';
299
300 1
		if ( $first_value ) {
301
			// find header for current value:
302 1
			if ( $this->mShowHeaders && ( '' != $pr->getLabel() ) ) {
303 1
				$header = $pr->getText( $outputmode, $this->mLinker ) . ': ';
304
			}
305
306
			// is this a start date?
307 1
			if ( ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) &&
308 1
				( $date_value == $this->m_tlstart ) ) {
309
				// FIXME: Timeline scripts should support XSD format explicitly. They
310
				// currently seem to implement iso8601 which deviates from XSD in cases.
311
				// NOTE: We can assume $object to be an SMWDataValue in this case.
312 1
				$curmeta .= Html::element(
313 1
					'span',
314 1
					[ 'class' => 'smwtlstart' ],
315 1
					$object->getXMLSchemaDate()
0 ignored issues
show
Bug introduced by Jeroen De Dauw
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getXMLSchemaDate() does only exist in the following sub-classes of SMWDataValue: SMWTimeValue. 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...
316
				);
317 1
				$positions[$object->getHash()] = $object->getXMLSchemaDate();
0 ignored issues
show
Bug introduced by Jeroen De Dauw
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getXMLSchemaDate() does only exist in the following sub-classes of SMWDataValue: SMWTimeValue. 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...
318 1
				$hastime = true;
319
			}
320
321
			// is this the end date?
322 1
			if ( ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) &&
323 1
				( $date_value == $this->m_tlend ) ) {
324
				// NOTE: We can assume $object to be an SMWDataValue in this case.
325
				$curmeta .= Html::element(
326
					'span',
327
					[ 'class' => 'smwtlend' ],
328
					$object->getXMLSchemaDate( false )
0 ignored issues
show
Bug introduced by Jeroen De Dauw
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getXMLSchemaDate() does only exist in the following sub-classes of SMWDataValue: SMWTimeValue. 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...
329
				);
330
			}
331
332
			// find title for displaying event
333 1
			if ( !$hastitle ) {
334 1
				$curmeta .= Html::rawElement(
335 1
					'span',
336
					[
337 1
						'class' => $urlobject ? 'smwtlurl' : 'smwtltitle'
338
					],
339 1
					$objectlabel
340
				);
341
342 1
				if ( $pr->getMode() == SMWPrintRequest::PRINT_THIS ) {
343 1
					$curarticle = $object->getLongText( $outputmode, $l );
344 1
					$cururl = $object->getDataItem()->getTitle()->getFullUrl();
0 ignored issues
show
Bug introduced by Stephan
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getTitle() does only exist in the following sub-classes of SMWDataItem: SMWDIWikiPage, SMW\DIWikiPage. 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...
345
				}
346
347
				// NOTE: type Title of $object implied
348 1
				$hastitle = true;
349
			}
350
		} elseif ( $output ) {
351
			// it *can* happen that output is false here, if the subject was not printed (fixed subject query) and mutliple items appear in the first row
352
			$curdata .= ', ';
353
		}
354
355 1
		if ( !$first_col || !$first_value || $isEventline ) {
356 1
			$curdata .= $header . $objectlabel;
357 1
			$output = true;
358
		}
359
360 1
		if ( $isEventline && ( $pr->getMode() == SMWPrintRequest::PRINT_PROP ) && ( $pr->getTypeID(
361 1
				) == '_dat' ) && ( '' != $pr->getLabel(
362 1
				) ) && ( $date_value != $this->m_tlstart ) && ( $date_value != $this->m_tlend ) ) {
363
			$event = [
364
				$object->getXMLSchemaDate(),
0 ignored issues
show
Bug introduced by Jeroen De Dauw
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getXMLSchemaDate() does only exist in the following sub-classes of SMWDataValue: SMWTimeValue. 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...
365
				$pr->getLabel(),
366
				$object->getDataItem()->getSortKey(),
367
			];
368
		}
369
370 1
		return $event;
371
	}
372
373
	/**
374
	 * @see SMWResultPrinter::getParamDefinitions
375
	 *
376
	 * @since 1.8
377
	 *
378
	 * @param $definitions array of IParamDefinition
379
	 *
380
	 * @return array of IParamDefinition|array
381
	 */
382 1
	public function getParamDefinitions( array $definitions ) {
383 1
		$params = parent::getParamDefinitions( $definitions );
384
385 1
		$params['timelinesize'] = [
386
			'default' => '300px',
387
			'message' => 'srf_paramdesc_timelinesize',
388
		];
389
390 1
		$params['timelineposition'] = [
391
			'default' => 'middle',
392
			'message' => 'srf_paramdesc_timelineposition',
393
			'values' => [ 'start', 'middle', 'end', 'today' ],
394
		];
395
396 1
		$params['timelinestart'] = [
397
			'default' => '',
398
			'message' => 'srf_paramdesc_timelinestart',
399
		];
400
401 1
		$params['timelineend'] = [
402
			'default' => '',
403
			'message' => 'srf_paramdesc_timelineend',
404
		];
405
406 1
		$params['timelinebands'] = [
407
			'islist' => true,
408
			'default' => [ 'MONTH', 'YEAR' ],
409
			'message' => 'srf_paramdesc_timelinebands',
410
			'values' => [ 'MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR', 'DECADE' ],
411
		];
412
413 1
		$params['template'] = [
414
			'message' => 'smw-paramdesc-template',
415
			'default' => '',
416
		];
417
418 1
		$params['named args'] = [
419
			'type' => 'boolean',
420
			'message' => 'smw-paramdesc-named_args',
421
			'default' => false,
422
		];
423
424 1
		return $params;
425
	}
426
427
}
428