Completed
Push — master ( 684184...9a35aa )
by Jeroen De
08:07
created

includes/Maps_DisplayMapRenderer.php (1 issue)

Labels
Severity

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
use Maps\Element;
3
use Maps\Elements\Line;
4
use Maps\Elements\Location;
5
6
/**
7
 * Class handling the #display_map rendering.
8
 *
9
 * @licence GNU GPL v2+
10
 * @author Jeroen De Dauw < [email protected] >
11
 * @author Kim Eik
12
 */
13
class MapsDisplayMapRenderer {
14
15
	/**
16
	 * @since 2.0
17
	 *
18
	 * @var iMappingService
19
	 */
20
	protected $service;
21
22
	/**
23
	 * Constructor.
24
	 *
25
	 * @param iMappingService $service
26
	 */
27
	public function __construct( iMappingService $service ) {
28
		$this->service = $service;
29
	}
30
31
	/**
32
	 * Returns the HTML to display the map.
33
	 *
34
	 * @since 2.0
35
	 *
36
	 * @param array $params
37
	 * @param Parser $parser
38
	 * @param string $mapName
39
	 *
40
	 * @return string
41
	 */
42
	protected function getMapHTML( array $params, Parser $parser, $mapName ) {
43
		return Html::rawElement(
44
			'div',
45
			[
46
				'id' => $mapName,
47
				'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;",
48
				'class' => 'maps-map maps-' . $this->service->getName()
49
			],
50
			wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() .
51
				Html::element(
52
					'div',
53
					[ 'style' => 'display:none', 'class' => 'mapdata' ],
54
					FormatJson::encode( $this->getJSONObject( $params, $parser ) )
55
				)
56
		);
57
	}
58
59
	/**
60
	 * Returns a PHP object to encode to JSON with the map data.
61
	 *
62
	 * @since 2.0
63
	 *
64
	 * @param array $params
65
	 * @param Parser $parser
66
	 *
67
	 * @return mixed
68
	 */
69
	protected function getJSONObject( array $params, Parser $parser ) {
70
		return $params;
71
	}
72
73
	/**
74
	 * Handles the request from the parser hook by doing the work that's common for all
75
	 * mapping services, calling the specific methods and finally returning the resulting output.
76
	 *
77
	 * @param array $params
78
	 * @param Parser $parser
79
	 *
80
	 * @return string
81
	 */
82
	public final function renderMap( array $params, Parser $parser ) {
83
		$this->handleMarkerData( $params, $parser );
84
85
		$mapName = $this->service->getMapId();
86
87
		$output = $this->getMapHTML( $params, $parser, $mapName );
88
89
		$configVars = Skin::makeVariablesScript( $this->service->getConfigVariables() );
90
91
		$this->service->addDependencies( $parser );
92
		$parser->getOutput()->addHeadItem( $configVars );
93
94
		return $output;
95
	}
96
97
	/**
98
	 * Converts the data in the coordinates parameter to JSON-ready objects.
99
	 * These get stored in the locations parameter, and the coordinates on gets deleted.
100
	 *
101
	 * FIXME: complexity
102
	 *
103
	 * @since 1.0
104
	 *
105
	 * @param array &$params
106
	 * @param Parser $parser
107
	 */
108
	protected function handleMarkerData( array &$params, Parser $parser ) {
109
		if ( is_object( $params['centre'] ) ) {
110
			$params['centre'] = $params['centre']->getJSONObject();
111
		}
112
113
		$parserClone = clone $parser;
114
115
		if ( is_object( $params['wmsoverlay'] ) ) {
116
			$params['wmsoverlay'] = $params['wmsoverlay']->getJSONObject();
117
		}
118
119
		$iconUrl = MapsMapper::getFileUrl( $params['icon'] );
120
		$visitedIconUrl = MapsMapper::getFileUrl( $params['visitedicon'] );
121
		$params['locations'] = [];
122
123
		/**
124
		 * @var Location $location
125
		 */
126
		foreach ( $params['coordinates'] as $location ) {
127
			$jsonObj = $location->getJSONObject( $params['title'], $params['label'], $iconUrl, '', '',$visitedIconUrl);
128
129
			$jsonObj['title'] = $parserClone->parse( $jsonObj['title'], $parserClone->getTitle(), new ParserOptions() )->getText();
130
			$jsonObj['text'] = $parserClone->parse( $jsonObj['text'], $parserClone->getTitle(), new ParserOptions() )->getText();
131
			if ( isset( $jsonObj['inlineLabel'] ) ) {
132
				$jsonObj['inlineLabel'] = strip_tags($parserClone->parse( $jsonObj['inlineLabel'], $parserClone->getTitle(), new ParserOptions() )->getText(),'<a><img>');
133
			}
134
135
			$hasTitleAndtext = $jsonObj['title'] !== '' && $jsonObj['text'] !== '';
136
			$jsonObj['text'] = ( $hasTitleAndtext ? '<b>' . $jsonObj['title'] . '</b><hr />' : $jsonObj['title'] ) . $jsonObj['text'];
137
			$jsonObj['title'] = strip_tags( $jsonObj['title'] );
138
139
			$params['locations'][] = $jsonObj;
140
		}
141
142
		unset( $params['coordinates'] );
143
144
		$this->handleShapeData( $params, $parserClone );
145
146
		if ( $params['mappingservice'] === 'openlayers' ) {
147
			$params['layers'] = self::evilOpenLayersHack( $params['layers'] );
148
		}
149
	}
150
151
	protected function handleShapeData( array &$params, Parser $parserClone ) {
152
		$textContainers = [
153
			&$params['lines'] ,
154
			&$params['polygons'] ,
155
			&$params['circles'] ,
156
			&$params['rectangles'],
157
			&$params['imageoverlays'], // FIXME: this is Google Maps specific!!
158
		];
159
160
		foreach ( $textContainers as &$textContainer ) {
161
			if ( is_array( $textContainer ) ) {
162
				foreach ( $textContainer as &$obj ) {
163
					if ( $obj instanceof Element ) {
164
						$obj = $obj->getArrayValue();
165
					}
166
167
					$obj['title'] = $parserClone->parse( $obj['title'] , $parserClone->getTitle() , new ParserOptions() )->getText();
168
					$obj['text'] = $parserClone->parse( $obj['text'] , $parserClone->getTitle() , new ParserOptions() )->getText();
169
170
					$hasTitleAndtext = $obj['title'] !== '' && $obj['text'] !== '';
171
					$obj['text'] = ( $hasTitleAndtext ? '<b>' . $obj['title'] . '</b><hr />' : $obj['title'] ) . $obj['text'];
172
					$obj['title'] = strip_tags( $obj['title'] );
173
				}
174
			}
175
		}
176
	}
177
178
	/**
179
	 * FIXME
180
	 *
181
	 * Temporary hack until the mapping service handling gets a proper refactor
182
	 * This kind of JS construction is also rather evil and should not be done at this point
183
	 *
184
	 * @since 3.0
185
	 * @deprecated
186
	 *
187
	 * @param string[] $layers
188
	 *
189
	 * @return string[]
190
	 */
191
	public static function evilOpenLayersHack( $layers ) {
192
		global $egMapsOLLayerGroups, $egMapsOLAvailableLayers;
193
194
		$layerDefs = [];
195
		$layerNames = [];
196
197
		foreach ( $layers as $layerOrGroup ) {
198
			$lcLayerOrGroup = strtolower( $layerOrGroup );
199
200
			// Layer groups. Loop over all items and add them if not present yet:
201
			if ( array_key_exists( $lcLayerOrGroup, $egMapsOLLayerGroups ) ) {
202
				foreach ( $egMapsOLLayerGroups[$lcLayerOrGroup] as $layerName ) {
203
					if ( !in_array( $layerName, $layerNames ) ) {
204
						if ( is_array( $egMapsOLAvailableLayers[$layerName] ) ) {
205
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName][0];
206
						}
207
						else {
208
							$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$layerName];
209
						}
210
						$layerNames[] = $layerName;
211
					}
212
				}
213
			}
214
			// Single layers. Add them if not present yet:
215
			elseif ( array_key_exists( $lcLayerOrGroup, $egMapsOLAvailableLayers ) ) {
216
				if ( !in_array( $lcLayerOrGroup, $layerNames ) ) {
217
					if ( is_array( $egMapsOLAvailableLayers[$lcLayerOrGroup] ) ) {
218
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup][0];
219
					}
220
					else {
221
						$layerDefs[] = 'new ' . $egMapsOLAvailableLayers[$lcLayerOrGroup];
222
					}
223
224
					$layerNames[] = $lcLayerOrGroup;
225
				}
226
			}
227
		}
228
229
		MapsMappingServices::getServiceInstance( 'openlayers' )->addLayerDependencies( self::getLayerDependencies( $layerNames ) );
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface iMappingService as the method addLayerDependencies() does only exist in the following implementations of said interface: MapsOpenLayers.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
230
231
		return $layerDefs;
232
	}
233
234
	/**
235
	 * FIXME
236
	 * @see evilOpenLayersHack
237
	 */
238
	private static function getLayerDependencies( array $layerNames ) {
239
		global $egMapsOLLayerDependencies, $egMapsOLAvailableLayers;
240
241
		$layerDependencies = [];
242
243
		foreach ( $layerNames as $layerName ) {
244
			if ( array_key_exists( $layerName, $egMapsOLAvailableLayers ) // The layer must be defined in php
245
				&& is_array( $egMapsOLAvailableLayers[$layerName] ) // The layer must be an array...
246
				&& count( $egMapsOLAvailableLayers[$layerName] ) > 1 // ...with a second element...
247
				&& array_key_exists( $egMapsOLAvailableLayers[$layerName][1], $egMapsOLLayerDependencies ) ) { //...that is a dependency.
248
				$layerDependencies[] = $egMapsOLLayerDependencies[$egMapsOLAvailableLayers[$layerName][1]];
249
			}
250
		}
251
252
		return array_unique( $layerDependencies );
253
	}
254
255
}
256