GoogleMapDataResponse   F
last analyzed

Complexity

Total Complexity 99

Size/Duplication

Total Lines 738
Duplicated Lines 2.03 %

Coupling/Cohesion

Components 2
Dependencies 14

Importance

Changes 0
Metric Value
wmc 99
lcom 2
cbo 14
dl 15
loc 738
rs 1.862
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A session_var_name() 0 4 1
A add_custom_google_map_session_data() 0 6 1
A set_custom_google_map_session_data() 0 7 2
A get_custom_google_map_session_data() 0 14 3
A clear_custom_google_map_session_data() 0 4 1
B init() 0 43 11
A setOwner() 0 4 1
A setTitle() 0 4 1
A setLng() 0 4 1
A setLat() 0 4 1
A setFilterCode() 0 4 1
A index() 0 4 1
A showemptymap() 0 4 1
A showpagepointsmapxml() 8 8 2
A showchildpointsmapxml() 7 7 2
A showdirectchildren() 0 7 2
A showsearchpoint() 0 20 5
B showpointbyid() 0 26 7
A showcustompagesmapxml() 0 6 1
A showcustomdosmapxml() 0 6 1
F showaroundmexml() 0 88 20
D updatemexml() 0 56 18
A GoogleMapController() 0 7 2
A makeXMLData() 0 16 1
A xml_sheet() 0 21 4
B quick_static_map() 0 30 6
A make_static_map_url_into_image() 0 15 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like GoogleMapDataResponse 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 GoogleMapDataResponse, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * The way the map works is that you open a page, which loads the initial page
5
 * with the map points being loaded as a separate XML doc.
6
 *
7
 * This controller returns the Google Map data XML sheet
8
 * You can show one point by adding ?i=123
9
 * Where 123 is the ID of a GoogleMapLocationsObject
10
 *
11
 * Here are other return options for the Map .... *
12
 *
13
 * 'index' / 'showemptymap' => map without anything on it, fallback
14
 *
15
 * 'showpagepointsmapxml' => show points from the current page
16
 *
17
 * 'showchildpointsmapxml' => show points from the child pages (all child pages)
18
 *
19
 * 'showdirectchildren' => show points from the child pages (direct ones only)
20
 *
21
 * 'showsearchpoint' =>
22
 *
23
 * 'showcustompagesmapxml' => these are sitetree elements loaded by session
24
 *
25
 * 'showcustomdosmapxml' =>
26
 *
27
 * 'showdataobjects' =>
28
 *
29
 * 'updatemexml' =>
30
 *
31
 * 'showaroundmexml' =>
32
 *
33
 * 'showpointbyid' => can also be more than one ID
34
 *
35
 */
36
37
class GoogleMapDataResponse extends Controller
38
{
39
    private static $session_var_prefix = "addCustomGoogleMap";
40
41
    private static $increase_factor_if_nothing_found = 0;
42
43
44
    /**
45
     * Default URL handlers - (Action)/(ID)/(OtherID)
46
     */
47
    private static $url_handlers = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
48
        '/$Action//$OwnerID/$Title/$Longitude/$Latitude/$FilterCode/$SecondFilterCode' => 'handleAction',
49
    );
50
51
52
53
54
55
    #################
56
    # SESSION MANAGEMENT
57
    #################
58
59
    protected static function session_var_name($filterCode = "")
60
    {
61
        return Config::inst()->get("GoogleMapDataResponse", "session_var_prefix")."_".$filterCode;
62
    }
63
64
    /**
65
     * @param Array $addCustomGoogleMapArrayNEW
66
     * @param string $filterCode
67
     *
68
     */
69
    public static function add_custom_google_map_session_data($addCustomGoogleMapArrayNEW, $filterCode = "")
70
    {
71
        $addCustomGoogleMapArrayOLD = Session::get(self::session_var_name($filterCode));
72
        $addCustomGoogleMapArrayNEW = array_merge($addCustomGoogleMapArrayOLD, $addCustomGoogleMapArrayNEW);
73
        Session::set(Session::get(self::session_var_name($filterCode), serialize($addCustomGoogleMapArrayNEW)));
0 ignored issues
show
Unused Code introduced by
The call to Session::get() has too many arguments starting with serialize($addCustomGoogleMapArrayNEW).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
Bug introduced by
The call to set() misses a required argument $val.

This check looks for function calls that miss required arguments.

Loading history...
74
    }
75
76
    /**
77
     *
78
     *
79
     * @param Array $addCustomGoogleMapArray
80
     * @param string $filterCode
81
     *
82
     */
83
    public static function set_custom_google_map_session_data($addCustomGoogleMapArray, $filterCode = "")
84
    {
85
        if (!is_array($addCustomGoogleMapArray)) {
86
            user_error("addCustomGoogleMapArray should be an array!");
87
        }
88
        Session::set(self::session_var_name($filterCode), serialize($addCustomGoogleMapArray));
89
    }
90
91
    /**
92
     * @param string $filterCode
93
     *
94
     * @return Array
95
     */
96
    public static function get_custom_google_map_session_data($filterCode = "")
97
    {
98
        $data = Session::get(self::session_var_name($filterCode));
99
        if (is_array($data)) {
100
            $addCustomGoogleMapArray = $data;
101
        } else {
102
            try {
103
                $addCustomGoogleMapArray = unserialize($data);
104
            } catch (Exception $e) {
105
                $addCustomGoogleMapArray = array();
106
            }
107
        }
108
        return $addCustomGoogleMapArray;
109
    }
110
111
    /**
112
     *
113
     * @param string $filterCode
114
     */
115
    public static function clear_custom_google_map_session_data($filterCode = "")
116
    {
117
        Session::clear(self::session_var_name($filterCode));
118
    }
119
120
121
122
123
124
125
126
    #################
127
    # BASICS
128
    #################
129
    /**
130
     * @inherited
131
     */
132
    private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
133
        'showemptymap' => true,
134
        'showpagepointsmapxml' => true,
135
        'showchildpointsmapxml' => true,
136
        'showdirectchildren' => true,
137
        'showsearchpoint' => true,
138
        'showcustompagesmapxml' => true,
139
        'showcustomdosmapxml' => true,
140
        'showdataobjects' => true,
141
        'updatemexml' => 'ADMIN',
142
        'showaroundmexml' => true,
143
        'showpointbyid' => true
144
    );
145
146
    /**
147
     * @var Array
148
     */
149
    private static $actions_without_owner = array(
150
        'showemptymap'
151
    );
152
153
    /**
154
     * The Page that is displaying the
155
     * @var SiteTree
156
     */
157
    protected $owner = null;
158
159
    /**
160
     * @var Float
161
     */
162
    protected $lng = 0;
163
164
    /**
165
     * @var Float
166
     */
167
    protected $lat = 0;
168
169
    /**
170
     * @var String
171
     */
172
    protected $title = "";
173
174
    /**
175
     * @var Stringsql define a variable
176
     */
177
    protected $filterCode = "";
178
179
    /**
180
     * @var String
181
     */
182
    protected $secondFilterCode = "";
183
184
    /**
185
     * @var GoogleMap
186
     */
187
    protected $map = null;
188
189
190
191
192
193
194
195
196
197
198
199
    #################
200
    # SET AND GET VARIABLES
201
    #################
202
203
    public function init()
204
    {
205
        parent::init();
206
        $id = 0;
207
        if ($this->request->param("OwnerID")) {
208
            $id = intval($this->request->param("OwnerID"));
209
        } elseif (isset($_GET["i"])) {
210
            $i = intval($_GET["i"]);
211
            $point = GoogleMapLocationsObject::get()->byID($i);
212
            if (!$point) {
213
                //New POINT
214
            } else {
215
                $id = $point->ParentID;
216
            }
217
        }
218
        if ($id) {
219
            $this->owner = SiteTree::get()->byID($id);
0 ignored issues
show
Documentation Bug introduced by
It seems like \SiteTree::get()->byID($id) can also be of type object<DataObject>. However, the property $owner is declared as type object<SiteTree>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
220
        }
221
        //HACK
222
        elseif (!$this->owner) {
223
            $this->owner = DataObject::get_one(
224
                'SiteTree',
225
                array("Title" => Convert::raw2sql($this->request->param("Title")))
226
            );
227
        }
228
        if ($this->owner || in_array($this->request->param("Action"), self::$actions_without_owner)) {
229
            //all ok
230
        } elseif (in_array($this->request->param("Action"), self::$actions_without_owner)) {
231
            //ok too
232
            $this->owner = DataObject::get_one('SiteTree');
233
        } else {
234
            user_error("no owner has been identified for GoogleMapDataResponse", E_USER_NOTICE);
235
        }
236
        //END HACK
237
        $this->title = urldecode($this->request->param("Title"));
238
        $this->lng = floatval($this->request->param("Longitude"));
239
        $this->lat = floatval($this->request->param("Latitude"));
240
        $this->filterCode = urldecode($this->request->getVar("filtercode"));
0 ignored issues
show
Documentation Bug introduced by
It seems like urldecode($this->request->getVar('filtercode')) of type string is incompatible with the declared type object<Stringsql> of property $filterCode.

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...
241
        $this->secondFilterCode = urldecode($this->request->getVar("secondfiltercode"));
242
        if (!$this->title && $this->owner) {
243
            $this->title = $this->owner->Title;
244
        }
245
    }
246
247
    /**
248
     * @param object $owner
249
     */
250
    public function setOwner($owner)
251
    {
252
        $this->owner = $owner;
253
    }
254
255
    /**
256
     * @param String $title
257
     */
258
    public function setTitle($title)
259
    {
260
        $this->title = $title;
261
    }
262
263
    /**
264
     * @param float $lng
265
     */
266
    public function setLng($lng)
267
    {
268
        $this->lng = $lng;
269
    }
270
271
    /**
272
     * @param Float $lat
273
     */
274
    public function setLat($lat)
275
    {
276
        $this->lat = $lat;
277
    }
278
279
    /**
280
     * @param string $filterCode
281
     */
282
    public function setFilterCode($filterCode)
283
    {
284
        $this->filterCode = $filterCode;
0 ignored issues
show
Documentation Bug introduced by
It seems like $filterCode of type string is incompatible with the declared type object<Stringsql> of property $filterCode.

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...
285
    }
286
287
288
289
290
291
292
293
294
295
296
297
    #################
298
    # ACTIONS
299
    #################
300
301
    /**
302
     * @param SS_HTTPRequest
303
     *
304
     * @return String (XML)
305
     */
306
    public function index($request)
307
    {
308
        return $this->showemptymap($request);
309
    }
310
311
    /**
312
     * @param SS_HTTPRequest
313
     *
314
     * @return String (XML)
315
     */
316
    public function showemptymap($request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
317
    {
318
        return $this->makeXMLData(null, null, $this->title, $this->title." "._t("GoogleMap.MAP", "map"));
319
    }
320
321
    /**
322
     * @param SS_HTTPRequest
323
     *
324
     * @return String (XML)
325
     */
326 View Code Duplication
    public function showpagepointsmapxml($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
327
    {
328
        $data = GoogleMapLocationsObject::get()->filter(array("ParentID" => $this->owner->ID));
329
        if ($data->count()) {
330
            return $this->makeXMLData(null, $data, $this->title, $this->title." "._t("GoogleMap.MAP", "map"));
0 ignored issues
show
Documentation introduced by
$data is of type object<DataList>, but the function expects a object<ArrayList>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
331
        }
332
        return $this->showemptymap($request);
333
    }
334
335
    /**
336
     * @param SS_HTTPRequest
337
     *
338
     * @return String (XML)
339
     */
340 View Code Duplication
    public function showchildpointsmapxml($request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
341
    {
342
        if ($children = $this->owner->getChildrenOfType($this->owner, null)) {
343
            return $this->makeXMLData($children, null, $this->title, $this->title." "._t("GoogleMap.MAP", "map"));
344
        }
345
        return $this->showemptymap($request);
346
    }
347
348
    /**
349
     * @param SS_HTTPRequest
350
     *
351
     * @return String (XML)
352
     */
353
    public function showdirectchildren($request)
354
    {
355
        if ($children = Provider::get()) {
356
            return $this->makeXMLData($children, null, $this->title, $this->title." "._t("GoogleMap.MAP", "map"));
357
        }
358
        return $this->showemptymap($request);
359
    }
360
361
    /**
362
     * @param SS_HTTPRequest
363
     *
364
     * @return String (XML)
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
365
     */
366
    public function showsearchpoint($request)
367
    {
368
        if ($this->lat && $this->lng) {
369
            $point = GoogleMapLocationsObject::create();
370
            $point->ParentID = $this->owner->ID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
371
            $point->Latitude = $this->lat;
0 ignored issues
show
Documentation introduced by
The property Latitude does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
372
            $point->Longitude = $this->lng;
0 ignored issues
show
Documentation introduced by
The property Longitude does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
373
            $point->CustomPopUpWindowTitle = $this->title;
0 ignored issues
show
Documentation introduced by
The property CustomPopUpWindowTitle does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
374
            if ($this->address) {
0 ignored issues
show
Documentation introduced by
The property address does not exist on object<GoogleMapDataResponse>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
375
                die("get address to do");
376
                $point->CustomPopUpWindowInfo = $this->address;
0 ignored issues
show
Unused Code introduced by
$point->CustomPopUpWindowInfo = $this->address; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
377
            }
378
            if ($point) {
379
                $data = new ArrayList();
380
                $data->push($point);
381
                return $this->makeXMLData(null, $data, $this->title, $this->title);
382
            }
383
        }
384
        return $this->showemptymap($request);
385
    }
386
387
    /**
388
     * @param SS_HTTPRequest
389
     *
390
     * @return String (XML)
391
     */
392
    public function showpointbyid($request)
393
    {
394
        if(! $this->filterCode) {
395
            $this->filterCode = $request->getVar("filtercode");
396
        }
397
        $ids = explode(',', $this->filterCode);
398
        foreach ($ids as $key => $id) {
399
            $ids[$key] = intval($id);
400
        }
401
        $className = Convert::raw2sql($request->getVar("secondfiltercode"));
402
        $direct = false;
403
        if (! $className) {
404
            $direct = true;
405
        } elseif (! class_exists($className)) {
406
            $direct = true;
407
        }
408
        if ($direct) {
409
            $className = "GoogleMapLocationsObject";
410
        }
411
        $objects = $className::get()->filter(array("ID" => $ids));
412
        if ($direct) {
413
            return $this->makeXMLData(null, $objects, $this->title, $this->title);
414
        } else {
415
            return $this->makeXMLData($objects, null, $this->title, $this->title);
416
        }
417
    }
418
419
420
    /**
421
     * load data from session
422
     *
423
     * @param SS_HTTPRequest
424
     *
425
     * @return String (XML)
426
     */
427
    public function showcustompagesmapxml($request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
428
    {
429
        $addCustomGoogleMapArray = GoogleMapDataResponse::get_custom_google_map_session_data($this->filterCode);
430
        $pages = SiteTree::get()->filter(array("ID" => $addCustomGoogleMapArray));
431
        return $this->makeXMLData($pages, null, $this->title, $this->title);
0 ignored issues
show
Documentation introduced by
$pages is of type object<DataList>, but the function expects a object<ArrayList>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
432
    }
433
434
    /**
435
     * load a custom set of GoogleMapLocationsObjects
436
     *
437
     * @param SS_HTTPRequest
438
     *
439
     * @return String (XML)
440
     */
441
    public function showcustomdosmapxml($request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
442
    {
443
        $array = GoogleMapDataResponse::get_custom_google_map_session_data($this->filterCode);
444
        $googleMapLocationsObjects = GoogleMapLocationsObject::get()->filter(array("ID" => $array));
445
        return $this->makeXMLData(null, $googleMapLocationsObjects, $this->title, $this->title);
0 ignored issues
show
Documentation introduced by
$googleMapLocationsObjects is of type object<DataList>, but the function expects a object<ArrayList>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
446
    }
447
448
    /**
449
     * Show what is around my points
450
     *
451
     * @param SS_HTTPRequest
452
     *
453
     * @return String (XML)
454
     */
455
    public function showaroundmexml($request)
456
    {
457
        $lng = 0;
458
        $lat = 0;
459
        $maxRadius = intval(Config::inst()->get('GoogleMap', 'max_radius_for_show_around_me')) - 0;
460
        if (isset($_GET['maxradius'])) {
461
            $maxRadius = intval($_GET['maxradius']);
462
        }
463
        $excludeIDList = array();
464
        $stage = '';
465
        if (Versioned::current_stage() === "Live") {
466
            $stage = "_Live";
467
        }
468
        if ($this->lng && $this->lat) {
469
            $lng = $this->lng;
470
            $lat = $this->lat;
471
        } elseif ($this->owner->ID) {
472
            //find the average!
473
            $objects = GoogleMapLocationsObject::get()->filter(array("ParentID" => $this->owner->ID));
474
            if ($count = $objects->count()) {
475
                foreach ($objects as $point) {
476
                    $lng += $point->Longitude;
477
                    $lat += $point->Latitude;
478
                }
479
                $lng = $lng / $count;
480
                $lat = $lat / $count;
481
            }
482
        }
483
        $classNameForParent = '';
484
        if ($otherClass = $this->filterCode) {
485
            $classNameForParent = $otherClass;
486
        }
487
        if ($this->title) {
488
            $title = $this->title;
489
        } else {
490
            $title = _t("GoogleMap.CLOSES_TO_ME", "Closest to me");
491
        }
492
        if ($lng && $lat) {
493
            $orderByRadius = GoogleMapLocationsObject::radius_definition($lng, $lat);
494
            $where = "(".$orderByRadius.") > 0 AND (".$orderByRadius.") < ".$maxRadius." AND \"GoogleMapLocationsObject\".\"Latitude\" <> 0 AND \"GoogleMapLocationsObject\".\"Longitude\" <> 0";
495
            if ($classNameForParent && !is_object($classNameForParent)) {
496
                $where .= " AND \"SiteTree".$stage."\".\"ClassName\" = '".$classNameForParent."'";
497
            }
498
            if (count($excludeIDList)) {
499
                $where .= " AND \"GoogleMapLocationsObject\".\"ID\" NOT IN (".implode(",", $excludeIDList).") ";
500
            }
501
            $objects = GoogleMapLocationsObject::get()
502
                ->where($where)
503
                ->sort($orderByRadius)
504
                ->leftJoin("SiteTree".$stage."", "SiteTree".$stage.".ID = GoogleMapLocationsObject.ParentID")
505
                ->limit(Config::inst()->get("GoogleMap", "number_shown_in_around_me"));
506
            if ($objects->count()) {
507
                $titlePrefix = Config::inst()->get("GoogleMap", "number_shown_in_around_me") . " "._t("GoogleMap.CLOSEST_POINTS", "closest points").' ';
508
            } else {
509
                $titlePrefix = _t('GoogleMap.NO_POINTS_SHOW_AROUND_ME', 'No locations found');
510
                $objects = null;
511
            }
512
        }
513
514
        if ($objects && $objects->count()) {
0 ignored issues
show
Bug introduced by
The variable $objects 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...
515
            $noObjectsMessage = '';
516
        } else {
517
            $increaseFactor = Config::inst()->get('GoogleMapDataResponse', 'increase_factor_if_nothing_found');
518
            if ($increaseFactor && $maxRadius < 20000) {
519
                $limit = Config::inst()->get("GoogleMap", "number_shown_in_around_me");
520
                Config::inst()->update('GoogleMap', 'max_radius_for_show_around_me', $maxRadius * $increaseFactor);
521
                Config::inst()->update("GoogleMap", "number_shown_in_around_me", $limit * $increaseFactor);
522
                return $this->showaroundmexml($request);
523
            }
524
            $noObjectsMessage = sprintf(
525
                _t(
526
                    'GoogleMap.NO_POINTS_SHOW_AROUND_ME',
527
                    'When searching for:<br/> <strong>%s</strong><br/> No locations (in a radius of '.$maxRadius.'km) were found.<br/><br/>'
528
                    .'<strong>Please try a more specific or different address.</strong>',
529
                    'Title'
530
                ),
531
                $title
532
            );
533
        }
534
535
        return $this->makeXMLData(
536
            null,
537
            $objects,
538
            $titlePrefix.$title,
0 ignored issues
show
Bug introduced by
The variable $titlePrefix 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...
539
            $titlePrefix,
540
            $noObjectsMessage
541
        );
542
    }
543
544
    /**
545
     * URL must contain for GET variables
546
     * i - ID of owner
547
     * a - action
548
     * x - lng
549
     * y - lat
550
     *
551
     * actions are:
552
     *   - add
553
     *   - move
554
     *   - remove
555
     *
556
     * @param SS_HTTPRequest
557
     *
558
     * @return String (message)
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
559
     */
560
    public function updatemexml($request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
561
    {
562
        //we use request here, because the data comes from javascript!
563
        if ($this->owner->canEdit()) {
564
            if (isset($_REQUEST["x"]) && isset($_REQUEST["y"]) && isset($_REQUEST["i"]) && isset($_REQUEST["a"])) {
565
                $lng = floatval($_REQUEST["x"]);
566
                $lat = floatval($_REQUEST["y"]);
567
                $id = intval($_REQUEST["i"]);
568
                $action = $_REQUEST["a"];
569
                if ($lng && $lat) {
570
                    if (0 == $id && "add" == $action) {
571
                        $point = new GoogleMapLocationsObject;
572
                        $point->ParentID = $this->owner->ID;
0 ignored issues
show
Documentation introduced by
The property ParentID does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
573
                        $point->Longitude = $lng;
0 ignored issues
show
Documentation introduced by
The property Longitude does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
574
                        $point->Latitude = $lat;
0 ignored issues
show
Documentation introduced by
The property Latitude does not exist on object<GoogleMapLocationsObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
575
                        $point->write();
576
                        return $point->ID;
577
                    } elseif ($id > 0 && "move" == $action) {
578
                        $point = GoogleMapLocationsObject::get()->byID($id);
579
                        if ($point) {
580
                            if ($point->ParentID == $this->owner->ID) {
581
                                $point->Longitude = $lng;
582
                                $point->Latitude = $lat;
583
                                $point->Address = "";
584
                                $point->FullAddress = "";
585
                                $point->write();
586
                                return  _t("GoogleMap.LOCATION_UPDATED", "location updated");
587
                            } else {
588
                                return _t("GoogleMap.NO_PERMISSION_TO_UPDATE", "you dont have permission to update that location");
589
                            }
590
                        } else {
591
                            return _t("GoogleMap.COULD_NOT_FIND_LOCATION", "could not find location");
592
                        }
593
                    } elseif ($id && "remove" == $action) {
594
                        $point = GoogleMapLocationsObject::get()->byID($id);
595
                        if ($point) {
596
                            if ($point->ParentID == $this->owner->ID) {
597
                                $point->delete();
598
                                $point = null;
0 ignored issues
show
Unused Code introduced by
$point is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
599
                                return _t("GoogleMap.LOCATION_DELETED", "location deleted");
600
                            } else {
601
                                return _t("GoogleMap.NO_DELETE_PERMISSION", "you dont have permission to delete that location");
602
                            }
603
                        } else {
604
                            return _t("GoogleMap.COULD_NOT_FIND_LOCATION", "could not find location.");
605
                        }
606
                    }
607
                } else {
608
                    return _t("GoogleMap.LOCATION_NOT_DEFINED", "point not defined.");
609
                }
610
            } else {
611
                return _t("GoogleMap.MISSING_VARIABLES", "not enough information was provided.");
612
            }
613
        }
614
        return  _t("GoogleMap.POINT_NOT_UPDATED", "You do not have permission to change the map.");
615
    }
616
617
618
619
620
621
622
623
    #################
624
    # TEMPLATE METHODS
625
    #################
626
    /**
627
     *
628
     * @return GoogleMap
629
     */
630
    public function GoogleMapController()
631
    {
632
        if (!$this->map) {
633
            user_error("No map has been created");
634
        }
635
        return $this->map;
636
    }
637
638
639
640
641
642
643
644
645
    #################
646
    # PRIVATE PARTY
647
    #################
648
649
    /**
650
     * @param ArrayList $pages
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pages not be ArrayList|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
651
     * @param ArrayList $dataPoints
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dataPoints not be ArrayList|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
652
     * @param string $title
653
     * @param string $selectionStatement
654
     * @param string $noDataPointsMessage
655
     *
656
     * @return String (XML)
657
     */
658
    protected function makeXMLData(
659
        $pages = null,
660
        $dataPoints = null,
661
        $title = '',
662
        $selectionStatement = '',
663
        $noDataPointsMessage = ''
664
    ) {
665
        $this->response->addHeader("Content-Type", "text/xml; charset=\"utf-8\"");
666
        return self::xml_sheet(
667
            $pages,
668
            $dataPoints,
669
            $title,
670
            $selectionStatement,
671
            $noDataPointsMessage
672
        );
673
    }
674
675
676
677
    ################################
678
    # STATIC METHODS
679
    ################################
680
    /**
681
     *
682
     * @param  ArrayList $pages you can also use the $dataPoints variable if you prefer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pages not be ArrayList|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
683
     * @param  ArrayList $dataPoints you can also use the $pages variable if you prefer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dataPoints not be ArrayList|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
684
     * @param  string $title
685
     * @param  string $selectionStatement
686
     * @param  string $noDataPointsMessage message to show when there are no data points ...
687
     * @return [type]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
688
     */
689
    public static function xml_sheet(
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
690
        $pages = null,
691
        $dataPoints = null,
692
        $title = '',
693
        $selectionStatement = '',
694
        $noDataPointsMessage = ''
695
    ) {
696
697
        //create a new one every time ...!
698
        $map = Injector::inst()->create("GoogleMap");
699
        $map->setTitleOfMap($title);
700
        $map->setWhereStatementDescription($selectionStatement ? $selectionStatement : $title);
701
        $map->setNoDataPointsMessage($noDataPointsMessage);
702
        if ($pages) {
703
            $map->setPageDataObjectSet($pages);
704
        } elseif ($dataPoints) {
705
            $map->setPoints($dataPoints);
706
        }
707
        $map->createDataPoints();
708
        return $map->renderWith("GoogleMapXml");
709
    }
710
711
712
713
    /**
714
     * var arrayOfLatitudeAndLongitude: Array (Latitude" => 123, "Longitude" => 123, "Marker" => "red1");
715
     * Marker is optional
716
     * @param Array arrayOfLatitudeAndLongitude
717
     * @param String title
718
     *
719
     * @return String (HTML - img tag)
720
     */
721
722
    public static function quick_static_map($arrayOfLatitudeAndLongitude, $title)
723
    {
724
        $staticMapURL = '';
0 ignored issues
show
Unused Code introduced by
$staticMapURL is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
725
        $count = 0;
726
        //width
727
        $staticMapWidth = Config::inst()->get("GoogleMap", "google_map_width");
728
        if ($staticMapWidth > 512) {
729
            $staticMapWidth = 512;
730
        }
731
        //height
732
        $staticMapHeight = Config::inst()->get("GoogleMap", "google_map_height");
733
        if ($staticMapHeight > 512) {
734
            $staticMapHeight = 512;
735
        }
736
        $staticMapURL = "size=".$staticMapWidth."x".$staticMapHeight;
737
        if (count($arrayOfLatitudeAndLongitude)) {
738
            //http://maps.google.com/maps/api/staticmap?sensor=true&maptype=map&size=209x310&
739
            //markers=color:green%7Clabel:A%7C-45.0302,168.663
740
            //&markers=color:red%7Clabel:Q%7C-36.8667,174.767
741
            foreach ($arrayOfLatitudeAndLongitude as $row) {
742
                $staticMapURL .= '&amp;markers=color:'.$row["Colour"].'%7Clabel:'.$row["Label"].'%7C';
743
                $staticMapURL .= round($row["Latitude"], 6).",".round($row["Longitude"], 6);
744
                $count++;
745
            }
746
            if ($count == 1) {
747
                $staticMapURL .= '&amp;center='.$defaultCenter.'&amp;zoom='. Config::inst()->get("GoogleMap", "default_zoom");
0 ignored issues
show
Bug introduced by
The variable $defaultCenter does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
748
            }
749
        }
750
        return self::make_static_map_url_into_image($staticMapURL, $title);
751
    }
752
753
    /**
754
     * @param String $staticMapURL
755
     * @param String $title
756
     *
757
     * @return String (HTML - img tag)
758
     */
759
    protected static function make_static_map_url_into_image($staticMapURL, $title)
760
    {
761
        $fullStaticMapURL =
762
            'http://maps.google.com/maps/api/staticmap?'
763
                .Config::inst()->get("GoogleMap", "static_map_settings").'&amp;'
764
                .$staticMapURL.'&amp;'
765
                .'key='.Config::inst()->get("GoogleMap", "google_map_api_key");
766
        if (Config::inst()->get("GoogleMap", "save_static_map_locally")) {
767
            $fileName = str_replace(array('&', '|', ',', '=', ';'), array('', '', '', '', ''), $staticMapURL);
768
            $length = strlen($fileName);
769
            $fileName = "_sGMap".substr(hash("md5", $fileName), 0, 35)."_".$length.".gif";
770
            $fullStaticMapURL = StaticMapSaverForHTTPS::convert_to_local_file(str_replace('&amp;', '&', $fullStaticMapURL), $fileName);
771
        }
772
        return '<img class="staticGoogleMap" src="'.$fullStaticMapURL.'" alt="map: '.Convert::raw2att($title).'" />';
773
    }
774
}
775