Completed
Push — master ( 9b2ed5...981814 )
by Jeroen De
76:07
created

formats/media/MediaPlayer.php (7 issues)

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 SRF;
4
5
use File;
6
use FormatJson;
7
use Html;
8
use Skin;
9
use SMW\ResultPrinter;
10
use SMWDataItem;
11
use SMWDataValue;
12
use SMWOutputs;
13
use SMWQueryResult;
14
use SRFUtils;
15
use Title;
16
17
/**
18
 * HTML5 Audio / Video media query printer
19
 *
20
 * This printer integrates jPlayer which is a HTML5 Audio / Video
21
 * Javascript libray under GPL/MIT license.
22
 *
23
 * @see http://www.semantic-mediawiki.org/wiki/Help:Media_format
24
 *
25
 * @since 1.9
26
 *
27
 * @file
28
 * @ingroup SRF
29
 * @ingroup QueryPrinter
30
 *
31
 * @licence GNU GPL v2 or later
32
 * @author mwjames
33
 */
34
35
/**
36
 * This printer integrates jPlayer which is a HTML5 Audio / Video
37
 * Javascript libray under GPL/MIT license.
38
 *
39
 * @ingroup SRF
40
 * @ingroup QueryPrinter
41
 */
42
class MediaPlayer extends ResultPrinter {
43
44
	/**
45
	 * Specifies valid mime types supported by jPlayer
46
	 *
47
	 * @var array
48
	 */
49
	protected $validMimeTypes = [ 'mp3', 'mp4', 'webm', 'webma', 'webmv', 'ogg', 'oga', 'ogv', 'm4v', 'm4a' ];
50
51
	/**
52
	 * @see SMWResultPrinter::getName
53
	 * @return string
54
	 */
55
	public function getName() {
56
		return $this->msg( 'srf-printername-media' )->text();
57
	}
58
59
	/**
60
	 * @see SMWResultPrinter::getResultText
61
	 *
62
	 * @param SMWQueryResult $result
63
	 * @param $outputMode
64
	 *
65
	 * @return string
66
	 */
67
	protected function getResultText( SMWQueryResult $result, $outputMode ) {
68
69
		// Data processing
70
		$data = $this->getResultData( $result, $outputMode );
71
72
		// Check if the data processing returned any results otherwise just bailout
73
		if ( $data === [] ) {
74
			if ( $this->params['default'] !== '' ) {
75
				return $this->params['default'];
76
			} else {
77
				$result->addErrors( [ $this->msg( 'srf-no-results' )->inContentLanguage()->text() ] );
78
				return '';
79
			}
80
		} else {
81
			// Return formatted results
82
			return $this->getFormatOutput( $data );
83
		}
84
	}
85
86
	/**
87
	 * Returns an array with data
88
	 *
89
	 * @since 1.9
90
	 *
91
	 * @param SMWQueryResult $result
92
	 * @param $outputMode
93
	 *
94
	 * @return array
95
	 */
96
	protected function getResultData( SMWQueryResult $result, $outputMode ) {
97
98
		$data = [];
99
100
		/**
101
		 * Get all values for all rows that belong to the result set
102
		 *
103
		 * @var SMWResultArray $rows
104
		 */
105
		while ( $rows = $result->getNext() ) {
106
			$rowData = [];
107
			$mediaType = null;
108
			$mimeType = null;
109
110
			/**
111
			 * @var SMWResultArray $field
112
			 * @var SMWDataValue $dataValue
113
			 */
114
			foreach ( $rows as $field ) {
115
116
				// Label for the current property
117
				$propertyLabel = $field->getPrintRequest()->getLabel();
118
119
				// Label for the current subject
120
				$subjectLabel = $field->getResultSubject()->getTitle()->getFullText();
121
122
				if ( $propertyLabel === '' || $propertyLabel === '-' ) {
123
					$propertyLabel = 'subject';
124
				} elseif ( $propertyLabel === 'poster' ) {
0 ignored issues
show
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
125
					// Label "poster" is a special case where we set the media type to video in order
126
					// to use the same resources that can display video and cover art
127
					// $data['mediaTypes'][] = 'video';
128
				}
129
130
				// Check if the subject itself is a media source
131
				if ( $field->getResultSubject()->getTitle()->getNamespace() === NS_FILE && $mimeType === null ) {
132
					list( $mediaType, $mimeType, $source ) = $this->getMediaSource(
133
						$field->getResultSubject()->getTitle()
134
					);
135
					$rowData[$mimeType] = $source;
136
				}
137
138
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
139
					// Get other data value item details
140
					$value = $this->getDataValueItem(
141
						$propertyLabel,
142
						$dataValue->getDataItem()->getDIType(),
143
						$dataValue,
144
						$mediaType,
145
						$mimeType,
146
						$rowData
147
					);
148
					$rowData[$propertyLabel] = $value;
149
				}
150
			}
151
152
			// Only select relevant source data that match the validMimeTypes
153
			if ( $mimeType !== '' && in_array( $mimeType, $this->validMimeTypes ) ) {
154
				$data['mimeTypes'][] = $mimeType;
155
				$data['mediaTypes'][] = $mediaType;
156
				$data[$subjectLabel] = $rowData;
0 ignored issues
show
The variable $subjectLabel 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...
157
			}
158
		}
159
160
		return $data;
161
	}
162
163
	/**
164
	 * Returns media source information
165
	 *
166
	 * @since 1.9
167
	 *
168
	 * @param Title $title
169
	 */
170
	private function getMediaSource( Title $title ) {
171
172
		// Find the file source
173
		$source = wfFindFile( $title );
174
		if ( $source ) {
175
			// $source->getExtension() returns ogg even though it is a ogv/oga (same goes for m4p) file
176
			// this doesn't help much therefore we do it ourselves
177
			$extension = $source->getExtension();
178
179
			if ( in_array( $extension, [ 'ogg', 'oga', 'ogv' ] ) ) {
180
				$extension = strtolower( substr( $source->getName(), strrpos( $source->getName(), '.' ) + 1 ) );
181
182
				// Xiph.Org recommends that .ogg only be used for Ogg Vorbis audio files
183
				$extension = $extension === 'ogg' ? 'oga' : $extension;
184
185
				$params = [ $extension === 'ogv' ? 'video' : 'audio', $extension, $source->getUrl() ];
186
			} elseif ( in_array( $extension, [ 'm4v', 'm4a', 'm4p' ] ) ) {
187
				$params = [ $extension === 'm4v' ? 'video' : 'audio', $extension, $source->getUrl() ];
188
			} else {
189
				list( $major, $minor ) = File::splitMime( $source->getMimeType() );
0 ignored issues
show
The assignment to $minor is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
190
				$params = [ $major, $extension, $source->getUrl() ];
191
			}
192
		} else {
193
			$params = [];
194
		}
195
		return $params;
196
	}
197
198
	/**
199
	 * Returns single data value item
200
	 *
201
	 * @since 1.9
202
	 *
203
	 * @param string $label
204
	 * @param integer $type
205
	 * @param SMWDataValue $dataValue
206
	 * @param string $mediaType
207
	 * @param string $mimeType
208
	 *
209
	 * @return mixed
210
	 */
211
	private function getDataValueItem( &$label, $type, SMWDataValue $dataValue, &$mediaType, &$mimeType, &$rowData ) {
212
213
		if ( $type == SMWDataItem::TYPE_WIKIPAGE && $dataValue->getTitle()->getNamespace() === NS_FILE ) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getTitle() does only exist in the following sub-classes of SMWDataValue: SMWWikiPageValue. 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...
214
215
			if ( $label === 'source' && $mimeType === null ) {
216
217
				// Identify the media source
218
				// and get media information
219
				list( $mediaType, $mimeType, $source ) = $this->getMediaSource( $dataValue->getTitle() );
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getTitle() does only exist in the following sub-classes of SMWDataValue: SMWWikiPageValue. 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...
220
				$label = $mimeType;
221
				return $source;
222
			} elseif ( $label === 'poster' ) {
223
				$mediaType = 'video';
224
225
				// Get the cover art image url
226
				$source = wfFindFile( $dataValue->getTitle() );
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getTitle() does only exist in the following sub-classes of SMWDataValue: SMWWikiPageValue. 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...
227
				return $source->getUrl();
228
			}
229
		}
230
231
		if ( $type == SMWDataItem::TYPE_URI ) {
232
233
			$source = $dataValue->getDataItem()->getURI();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getURI() does only exist in the following sub-classes of SMWDataItem: SMWDIUri. 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...
234
			$mimeType = '';
235
236
			// Get file extension from the URI
237
			$extension = strtolower( substr( $source, strrpos( $source, '.' ) + 1 ) );
238
239
			// Xiph.Org recommends that .ogg only be used for Ogg Vorbis audio files
240
			if ( in_array( $extension, [ 'ogg', 'oga', 'ogv' ] ) ) {
241
				$mimeType = $extension === 'ogg' ? 'oga' : $extension;
242
				$mediaType = $extension === 'ogv' ? 'video' : 'audio';
243
			} elseif ( in_array( $extension, [ 'm4v', 'm4a', 'm4p' ] ) ) {
244
				$mimeType = $extension;
245
				$mediaType = $extension === 'm4v' ? 'video' : 'audio';
246
			} else {
247
				$mimeType = $extension;
248
				$mediaType = strpos( $extension, 'v' ) !== false ? 'video' : 'audio';
249
			}
250
251
			if ( $mimeType !== '' ) {
252
				$rowData[$mimeType] = $source;
253
			}
254
255
			return $source;
256
		}
257
258
		return $dataValue->getWikiValue();
259
	}
260
261
	/**
262
	 * Prepare data for the output
263
	 *
264
	 * @since 1.9
265
	 *
266
	 * @param array $data
267
	 *
268
	 * @return string
269
	 */
270
	protected function getFormatOutput( $data ) {
271
272
		$ID = 'srf-' . uniqid();
273
		$this->isHTML = true;
274
275
		// Get the media/mime types
276
		if ( in_array( 'video', $data['mediaTypes'] ) ) {
277
			$mediaType = 'video';
278
		} else {
279
			$mediaType = 'audio';
280
		}
281
		unset( $data['mediaTypes'] );
282
283
		$mimeTypes = array_unique( $data['mimeTypes'] );
284
		unset( $data['mimeTypes'] );
285
286
		// Reassign output array
287
		$output = [
288
			'data' => $data,
289
			'count' => count( $data ),
290
			'mediaType' => $mediaType,
291
			'mimeTypes' => implode( ',', $mimeTypes ),
292
			'inspector' => $this->params['inspector']
293
		];
294
295
		$requireHeadItem = [ $ID => FormatJson::encode( $output ) ];
296
		SMWOutputs::requireHeadItem( $ID, Skin::makeVariablesScript( $requireHeadItem ) );
297
298
		SMWOutputs::requireResource( 'ext.jquery.jplayer.skin.' . $this->params['theme'] );
299
		SMWOutputs::requireResource( 'ext.srf.formats.media' );
300
301
		$processing = SRFUtils::htmlProcessingElement();
302
303
		return Html::rawElement(
304
			'div',
305
			[
306
				'class' => $this->params['class'] !== '' ? 'srf-media ' . $this->params['class'] : 'srf-media'
307
			],
308
			$processing . Html::element(
309
				'div',
310
				[
311
					'id' => $ID,
312
					'class' => 'media-container',
313
					'style' => 'display:none;'
314
				],
315
				null
316
			)
317
		);
318
	}
319
320
	/**
321
	 * @see SMWResultPrinter::getParamDefinitions
322
	 *
323
	 * @since 1.9
324
	 *
325
	 * @param $definitions array of IParamDefinition
326
	 *
327
	 * @return array of IParamDefinition|array
328
	 */
329
	public function getParamDefinitions( array $definitions ) {
330
		$params = parent::getParamDefinitions( $definitions );
331
332
		$params['class'] = [
333
			'message' => 'srf-paramdesc-class',
334
			'default' => '',
335
		];
336
337
		$params['theme'] = [
338
			'message' => 'srf-paramdesc-theme',
339
			'default' => 'blue.monday',
340
			'values' => [ 'blue.monday', 'morning.light' ],
341
		];
342
343
		$params['inspector'] = [
344
			'type' => 'boolean',
345
			'message' => 'srf-paramdesc-mediainspector',
346
			'default' => false,
347
		];
348
349
		return $params;
350
	}
351
}
352