Completed
Push — master ( 2e6333...1c8f27 )
by Tim
17:33
created

Popup::arrayMergeRecursiveOverrule()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 21
rs 8.7624
cc 6
eloc 14
nc 6
nop 3
1
<?php
2
/***************************************************************
3
 *  Copyright notice
4
 *
5
 *  (c) 2009 Tim Lochmueller ([email protected])
6
 *  All rights reserved
7
 *
8
 *  This script is part of the Typo3 project. The Typo3 project is
9
 *  free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  (at your option) any later version.
13
 *
14
 *  The GNU General Public License can be found at
15
 *  http://www.gnu.org/copyleft/gpl.html.
16
 *
17
 *  This script is distributed in the hope that it will be useful,
18
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 *  GNU General Public License for more details.
21
 *
22
 *  This copyright notice MUST APPEAR in all copies of the script!
23
 ***************************************************************/
24
25
namespace FRUIT\Popup;
26
27
use TYPO3\CMS\Core\Utility\ArrayUtility;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Core\Utility\MathUtility;
30
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
31
use TYPO3\CMS\Frontend\Page\PageRepository;
32
33
/**
34
 * base class for the popup extension
35
 *
36
 * @author     Tim Lochmueller <[email protected]>
37
 * @package    popup
38
 * @subpackage tx_popup
39
 */
40
class Popup
41
{
42
43
    /**
44
     * cObj for the RTE replacement
45
     *
46
     * @var ContentObjectRenderer
47
     */
48
    public $cObj;
49
50
    /**
51
     * init the convert functions
52
     */
53
    protected $convertParams = [];
54
55
    /**
56
     * Possible popup parameter
57
     */
58
    public $allowedParams = [
59
        'height' => 'integer',
60
        'width' => 'integer',
61
        'resizable' => 'boolean',
62
        'scrollbars' => 'boolean',
63
        'menubar' => 'boolean',
64
        'status' => 'boolean',
65
        'location' => 'boolean',
66
        'toolbar' => 'boolean',
67
        'dependent' => 'boolean',
68
        'top' => 'integer',
69
        'left' => 'integer',
70
    ];
71
72
    /**
73
     * Possible popup parameter
74
     */
75
    public $advancedParams = [
76
        'once_per_session' => 'boolean',
77
        'once_per_link' => 'boolean',
78
        'center' => 'boolean',
79
        'maximize' => 'boolean',
80
        'popunder' => 'boolean',
81
    ];
82
83
    /**
84
     * Static Funktion for hooking the menu link generation
85
     *
86
     * @param    array|int $pageOrPageID The Page row or the page ID
87
     * @param    array $LD A reference of the Link Data
88
     */
89
    public static function makeMenuLink($pageOrPageID, &$LD)
90
    {
91
        $popup = GeneralUtility::makeInstance('FRUIT\\Popup\\Popup');
92
        if ($target = $popup->getPopupTarget($pageOrPageID)) {
93
            $LD['target'] = $target;
94
        }
95
    } # function - makeMenuLink
96
97
98
    /**
99
     * Get the target of a page
100
     *
101
     * @param    array|integer $pageOrPageID The Page row or the page ID
102
     *
103
     * @return string
104
     */
105
    public function getPopupTarget($pageOrPageID)
106
    {
107
        if (MathUtility::canBeInterpretedAsInteger($pageOrPageID)) {
108
            $pageOrPageID = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($GLOBALS['TYPO3_DB']->exec_SELECTquery(
109
                '*',
110
                'pages',
111
                'uid=' . $pageOrPageID,
112
                '',
113
                '',
114
                1
115
            ));
116
        } # if
117
118
        if (!is_array($pageOrPageID) || isset($pageOrPageID['tx_popup_configuration']) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by Tim Lochmüller
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
119
            return '';
120
        }
121
122
        $config = $pageOrPageID['tx_popup_configuration'];
123
        if (!strlen(trim($config))) {
124
            return '';
125
        }
126
        return $config;
127
    } # function - getPopupTarget
128
129
130
    /**
131
     * Return the default configuration
132
     *
133
     * @param      $page
134
     * @param bool $advanced
135
     *
136
     * @return    array    The default configuration
137
     */
138
    public function getDefaultConfiguration($page, $advanced = false)
139
    {
140
        $config = $this->loadTypoScript($page);
141
142
        if (!isset($config['allowedParams.'])) {
143
            return [];
144
        }
145
        $c = $config['allowedParams.'];
146
        if ($advanced && isset($config['advancedParams.'])) {
147
            $c = array_merge($c, $config['advancedParams.']);
148
        }
149
150
        return $c;
151
    } # function - getDefaultConfiguration
152
153
154
    /**
155
     * Text parser for internal popup links
156
     *
157
     * @author Tim Lochmueller
158
     * @author before 2009 Mathias Schreiber, Rene Fritz
159
     */
160
    public function textParse($content, $conf)
161
    {
162
163
        // ################
164
        // Default vars
165
        // Noch auslagern und f�r beides benutzen
166
        // #########################
167
168
        $JSpopup['link'] = $content['url'];
0 ignored issues
show
Coding Style Comprehensibility introduced by Tim Lochmüller
$JSpopup was never initialized. Although not strictly required by PHP, it is generally a good practice to add $JSpopup = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
169
        $JSpopup['ATagParams'] = $content['ATagParams'];
170
        $JSpopup['windowname'] = 'Easy Popup';
171
172
        $JSpopup['properties']['dependent'] = 'yes';
173
        $JSpopup['properties.']['height'] = '300';
174
        $JSpopup['properties.']['left'] = '10';
175
        $JSpopup['properties.']['location'] = 'yes';
176
        $JSpopup['properties.']['menubar'] = 'no';
177
        $JSpopup['properties.']['resizable'] = 'yes';
178
        $JSpopup['properties.']['scrollbars'] = 'yes';
179
        $JSpopup['properties.']['status'] = 'no';
180
        $JSpopup['properties.']['toolbar'] = 'no';
181
        $JSpopup['properties.']['top'] = '10';
182
        $JSpopup['properties.']['width'] = '300';
183
184
185
        $linkContent = '';
186
        if ($conf['link'] or is_array($conf['link.'])) {
187
            $conf['link'] = $this->cObj->stdWrap($conf['link'], $conf['link.']);
188
            $conf['ATagParams'] = $this->cObj->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
189
            $conf['windowname'] = $this->cObj->stdWrap($conf['windowname'], $conf['windowname.']);
190
191
            $JSpopup = $this->arrayMergeRecursiveOverrule($JSpopup, $conf, true);
0 ignored issues
show
Documentation introduced by Tim Lochmüller
true is of type boolean, but the function expects a integer.

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...
192
193
            $linkContent = $this->cObj->stdWrap($conf['linkContent'], $conf['linkContent.']);
194
        } else {
195
            if (isset($this->cObj->parameters['slim'])) {
196
                $JSpopup['properties.']['location'] = 'no';
197
                $JSpopup['properties.']['menubar'] = 'no';
198
                $JSpopup['properties.']['status'] = 'no';
199
                $JSpopup['properties.']['toolbar'] = 'no';
200
            }
201
            if (isset($this->cObj->parameters['fixed'])) {
202
                $JSpopup['properties.']['resizable'] = 'no';
203
                $JSpopup['properties.']['scrollbars'] = 'no';
204
            }
205
            $JSpopup['properties.'] = $this->arrayMergeRecursiveOverrule(
206
                $JSpopup['properties.'],
207
                $this->cObj->parameters,
208
                true
0 ignored issues
show
Documentation introduced by Tim Lochmüller
true is of type boolean, but the function expects a integer.

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...
209
            );
210
        }
211
212
        $temp = [];
213
        while (list($key, $val) = each($JSpopup['properties.'])) {
214
            $temp[] = $key . '=' . $val;
215
        }
216
217
        $props = implode(',', $temp);
218
219
        if (!$JSpopup['link']) {
220
            return '';
221
        }
222
223
        $TAG = '<a href="' . $JSpopup['link'] . '" onClick="openPic(this.href,\'' . str_replace(
224
            ' ',
225
            '',
226
            $JSpopup['windowname']
227
        ) . '\',\'' . $props . '\'); return false;" class="linkpop"' . $JSpopup['ATagParams'] . '>';
228
        if ($linkContent) {
229
            $TAG .= $linkContent . '</a>';
230
        }
231
232
        return $TAG;
233
    } # function - textParse
234
235
236
    /**
237
     *  Merges two arrays recursively, overruling the values of the first array
238
     *  in case of identical keys, ie. keeping the values of the second.
239
     */
240
    public function arrayMergeRecursiveOverrule($arr0, $arr1, $notAddKeys = 0)
241
    {
242
        reset($arr1);
243
        while (list($key, $val) = each($arr1)) {
244
            if (is_array($arr0[$key])) {
245
                if (is_array($arr1[$key])) {
246
                    ArrayUtility::mergeRecursiveWithOverrule($arr0[$key], $arr1[$key], $notAddKeys);
0 ignored issues
show
Documentation introduced by Tim Lochmüller
$notAddKeys is of type integer, but the function expects a boolean.

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...
247
                }
248
            } else {
249
                if ($notAddKeys) {
250
                    if (isset($arr0[$key])) {
251
                        $arr0[$key] = $val;
252
                    }
253
                } else {
254
                    $arr0[$key] = $val;
255
                }
256
            }
257
        }
258
        reset($arr0);
259
        return $arr0;
260
    }
261
262
263
    /**
264
     * Load TypoScript for the backend
265
     * This is important for the single configuration concept in the popup extension
266
     */
267
    public function loadTypoScript($pid, $pluginExtKey = 'tx_popup_pi1')
268
    {
269
270
        $sysPageObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
271
        $rootLine = $sysPageObj->getRootLine($pid);
272
        $TSObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\ExtendedTemplateService');
273
        $TSObj->tt_track = 0;
274
        $TSObj->init();
275
        $TSObj->runThroughTemplates($rootLine);
276
        $TSObj->generateConfig();
277
        return $TSObj->setup['plugin.'][$pluginExtKey . '.'];
278
    }
279
280
281
    /**
282
     * Start the convertion prcess
283
     * Init this process mit the parameter for the convertion
284
     * all iligal parameter will be removed
285
     */
286
    public function convertInit($params)
287
    {
288
        $this->convertParams = $params;
289
    }
290
291
292
    /**
293
     * Convert a TYPO3 Popup configuration String to an Array
294
     *
295
     * @param    string $configString The T3 configuration string (Like "width:height:param1=value,param2=value2")
296
     * @param    boolean $advanced If set, the advanced parameter will be included in the array
297
     *
298
     * @return    array
299
     */
300
    public function convertCfg2Array($configString, $advanced = false)
301
    {
302
        $params = $this->allowedParams;
303
        if ($advanced) {
304
            $params = array_merge($params, $this->advancedParams);
305
        }
306
307
        $parts = explode(':', $configString);
308
        if (sizeof($parts) != 2) {
309
            return false;
0 ignored issues
show
Bug Best Practice introduced by Tim Lochmüller
The return type of return false; (false) is incompatible with the return type documented by FRUIT\Popup\Popup::convertCfg2Array of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
310
        }
311
        $size = explode('x', $parts[0]);
312
        if (sizeof($size) != 2) {
313
            return false;
0 ignored issues
show
Bug Best Practice introduced by Tim Lochmüller
The return type of return false; (false) is incompatible with the return type documented by FRUIT\Popup\Popup::convertCfg2Array of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
314
        }
315
316
        $config = $this->convertJs2Array('width=' . $size[0] . ',height=' . $size[1] . ',' . $parts[1], $params);
0 ignored issues
show
Documentation introduced by Tim Lochmüller
$params is of type array, but the function expects a string.

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...
317
        return $this->setAllFields($params, $config);
318
    }
319
320
321
    /**
322
     * Convert JavaScript Params to an array
323
     *
324
     * @param string $string
325
     * @param string $params Javascript param String (Like "param1=value,param2=value2")
326
     *
327
     * @return    array
328
     */
329
    public function convertJs2Array($string, $params)
330
    {
331
        $config_pre = explode(',', $string);
332
        $config = [];
333
        foreach ($config_pre as $check) {
334
            $p = explode('=', $check);
335
            switch ($params[$p[0]]) {
336
                case 'integer':
337
                    $config[$p[0]] = intval($p[1]);
338
                    break;
339
                case 'boolean':
340
                    $config[$p[0]] = ($p[1] == "yes") ? true : false;
341
                    break;
342
            }
343
        }
344
        return $config;
345
    }
346
347
348
    /**
349
     * Convert an array to a TYPO3 Popup configuration String
350
     *
351
     * @param array $array Data Array of the field (key value pairs)
352
     * @param boolean $advanced If set, the advanced parameter will be included in the array
353
     *
354
     * @return string
355
     */
356
    public function convertArray2Cfg($array, $advanced = false)
357
    {
358
        $params = $this->allowedParams;
359
        if ($advanced) {
360
            $params = array_merge($params, $this->advancedParams);
361
        }
362
363
        $array = $this->setAllFields($params, $array);
364
365
        $cfg = '';
366
        if (isset($array['height']) && isset($array['width'])) {
367
            $cfg = $array['width'] . 'x' . $array['height'] . ':';
368
            unset($array['width']);
369
            unset($array['height']);
370
        }
371
372
        foreach ($array as $key => $value) {
373
            switch ($params[$key]) {
374
                case 'integer':
375
                    $cfg .= $key . '=' . intval($value);
376
                    break;
377
                case 'boolean':
378
                    $cfg .= $key . '=' . (($value) ? 'yes' : 'no');
379
                    break;
380
            } # switch
381
            $cfg .= ',';
382
        } # froeach
383
384
        return $cfg;
385
    } # function - convertArray2Cfg
386
387
388
    /**
389
     * Convert an array to a JS configuration String
390
     */
391
    public function convertArray2Js($array)
392
    {
393
        $params = $this->allowedParams;
394
        $array = $this->setAllFields($params, $array);
395
396
        $out = '';
397
        foreach ($array as $key => $value) {
398
            $out .= ($out != '') ? ',' : '';
399
400
            switch ($params[$key]) {
401
                case 'integer':
402
                    $out .= $key . '=' . intval($value);
403
                    break;
404
                case 'boolean':
405
                    $out .= $key . '=' . (($value) ? 'yes' : 'no');
406
                    break;
407
            } # switch
408
        } # foreach
409
410
        return $out;
411
    } # function - convertArray2Js
412
413
414
    /**
415
     * Convert a TYPO3 Popup configuration String to a JS configuration String
416
     */
417
    public function convertCfg2Js($string)
418
    {
419
        return $this->convertArray2Js($this->convertCfg2Array($string));
420
    } # function - convertCfg2Js
421
422
423
    /**
424
     * Convert a JS configuration string to a TYPO3 Popup configuration
425
     */
426
    public function convertJs2Cfg($string)
427
    {
428
        return $this->convertArray2Cfg($this->convertJs2Array($string, []));
0 ignored issues
show
Documentation introduced by Tim Lochmüller
array() is of type array, but the function expects a string.

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...
429
    } # function - convertJs2Cfg
430
431
432
    /**
433
     * Set all fields in the given configuration by the param parameter
434
     */
435
    protected function setAllFields($params, $config)
436
    {
437
        $param = [];
0 ignored issues
show
Unused Code introduced by Tim Lochmüller
$param 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...
438
        foreach (array_keys($params) as $p) {
439
            if (!in_array($p, array_keys($config))) {
440
                switch ($params[$p]) {
441
                    case 'integer':
442
                        $config[$p] = 0;
443
                        break;
444
                    case 'boolean':
445
                        $config[$p] = false;
446
                        break;
447
                } # switch
448
            } # if
449
        } # foreach
450
        return $config;
451
    } # function - setAllFields
452
}
453