Store.php 25.4 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 1/1/16
 * Time: 6:51 PM
 */

9
namespace qfq;
10

11
use qfq\CodeException;
12
13
use qfq\keyValueStringParser;
use qfq\OnArray;
14
use qfq;
15

16
require_once(__DIR__ . '/../../qfq/helper/KeyValueStringParser.php');
Carsten  Rose's avatar
Carsten Rose committed
17
require_once(__DIR__ . '/../../qfq/helper/Sanitize.php');
18
require_once(__DIR__ . '/../../qfq/Constants.php');
19
require_once(__DIR__ . '/../../qfq/store/Sip.php');
20
//require_once(__DIR__ . '/../../qfq/store/Session.php');
21
require_once(__DIR__ . '/../../qfq/Database.php');
22
23
24
25
26
27
28
29
30
31

/*
 * Stores:
 * - SIP
 * - webVar
 * - record
 * - form
 * - formElement
 */

Carsten  Rose's avatar
Carsten Rose committed
32
33
/**
 * Class Store
34
 * @package qfq
Carsten  Rose's avatar
Carsten Rose committed
35
 */
36
37
class Store {

Carsten  Rose's avatar
Carsten Rose committed
38
39
40
    /**
     * @var Store Instance of class Store. There should only be one class 'Store' at a time.
     */
41
42
    private static $instance = null;

Carsten  Rose's avatar
Carsten Rose committed
43
44
45
    /**
     * @var Sip Instance of class SIP
     */
46
47
    private static $sip = null;

48
49
50
    /**
     * @var Session Instance of class Session
     */
51
//    private static $session = null;
52

Carsten  Rose's avatar
Carsten Rose committed
53
54
55
56
57
58
59
60
61
62
    /**
     * @var array Stores all indiviudal stores with the variable raw values
     *
     * $raw['D']['id'] = 0  - Defaultvalues from Tabledefinition
     * ...
     * $raw['S']['r'] = 1234 - record ID from current SIP identifier
     * ...
     * $raw['C']['HTTP_SERVER'] = 'qfq' - Servername
     * $raw['C']['s'] = 'badcaffee1234' - recent SIP
     */
63
    private static $raw = array();
Carsten  Rose's avatar
Carsten Rose committed
64
65

    /**
Carsten  Rose's avatar
Carsten Rose committed
66
     * @var array Default sanitize classes.
Carsten  Rose's avatar
Carsten Rose committed
67
     */
Carsten  Rose's avatar
Carsten Rose committed
68
    private static $sanitizeClass = array();
Carsten  Rose's avatar
Carsten Rose committed
69
70

    /**
Carsten  Rose's avatar
Carsten Rose committed
71
72
     * $sanitizeClass['S'] = false
     * $sanitizeClass['C'] = true
Carsten  Rose's avatar
Carsten Rose committed
73
74
     * ...
     *
Carsten  Rose's avatar
Carsten Rose committed
75
     * @var array each entry with true/false - depending if store needs to be sanitized.
Carsten  Rose's avatar
Carsten Rose committed
76
     */
Carsten  Rose's avatar
Carsten Rose committed
77
    private static $sanitizeStore = array();
78

Carsten  Rose's avatar
Carsten Rose committed
79
    private static $phpUnit = false;
80

81

82
    /**
83
     * @param string $bodytext
84
     */
85
    private function __construct($bodytext = '', $fileConfigIni = '') {
86

87
//        self::$session = Session::getInstance(self::$phpUnit);
88

Carsten  Rose's avatar
Carsten Rose committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
        self::$sanitizeClass = [
//            TYPO3_DEBUG_LOAD => SANITIZE_ALLOW_DIGIT,
//            TYPO3_DEBUG_SAVE => SANITIZE_ALLOW_DIGIT,
//            TYPO3_FORM => SANITIZE_ALLOW_ALNUMX,
//            TYPO3_FE_USER => SANITIZE_ALLOW_ALNUMX,
//            TYPO3_FE_USER_UID => SANITIZE_ALLOW_DIGIT,
//            TYPO3_FE_USER_GROUP => SANITIZE_ALLOW_ALNUMX,

            CLIENT_SIP => SANITIZE_ALLOW_ALNUMX,
            CLIENT_RECORD_ID => SANITIZE_ALLOW_DIGIT,
            CLIENT_KEY_SEM_ID => SANITIZE_ALLOW_DIGIT,
            CLIENT_KEY_SEM_ID_USER => SANITIZE_ALLOW_DIGIT,
            CLIENT_PAGE_ID => SANITIZE_ALLOW_DIGIT,
            CLIENT_PAGE_TYPE => SANITIZE_ALLOW_DIGIT,
            CLIENT_PAGE_LANGUAGE => SANITIZE_ALLOW_DIGIT,
            CLIENT_FORM => SANITIZE_ALLOW_ALNUMX,

            // Part of $_SERVER. Missing vars must be requested individual with the needed sanitize class.
            CLIENT_SCRIPT_URL => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SCRIPT_URI => SANITIZE_ALLOW_ALNUMX,
            CLIENT_HTTP_HOST => SANITIZE_ALLOW_ALNUMX,
            CLIENT_HTTP_USER_AGENT => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SERVER_NAME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SERVER_ADDRESS => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SERVER_PORT => SANITIZE_ALLOW_DIGIT,
            CLIENT_REMOTE_ADDRESS => SANITIZE_ALLOW_ALNUMX,
            CLIENT_REQUEST_SCHEME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SCRIPT_FILENAME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_QUERY_STRING => SANITIZE_ALLOW_ALL,
            CLIENT_REQUEST_URI => SANITIZE_ALLOW_ALL,
            CLIENT_SCRIPT_NAME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_PHP_SELF => SANITIZE_ALLOW_ALNUMX,
121
//            CLIENT_UPLOAD_FILENAME => SANITIZE_ALLOW_ALLBUT,
Carsten  Rose's avatar
Carsten Rose committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

//            SYSTEM_DBUSER => SANITIZE_ALLOW_ALNUMX,
//            SYSTEM_DBSERVER => SANITIZE_ALLOW_ALNUMX,
//            SYSTEM_DBPW => SANITIZE_ALLOW_ALL,
//            SYSTEM_DB => SANITIZE_ALLOW_ALNUMX,
//            SYSTEM_TESTDB => SANITIZE_ALLOW_ALNUMX,
//            SYSTEM_SESSIONNAME => SANITIZE_ALLOW_ALNUMX,
//            SYSTEM_DBH => SANITIZE_ALLOW_ALL,

//            SYSTEM_SQL_RAW => SANITIZE_ALLOW_ALL,
//            SYSTEM_SQL_FINAL => SANITIZE_ALLOW_ALL,
//            SYSTEM_SQL_COUNT => SANITIZE_ALLOW_DIGIT,
//            SYSTEM_SQL_PARAM_ARRAY => SANITIZE_ALLOW_ALL,

//            SIP_SIP => SANITIZE_ALLOW_ALNUMX,
//            SIP_RECORD_ID => SANITIZE_ALLOW_DIGIT,
//            SIP_FORM => SANITIZE_ALLOW_ALNUMX,
//            SIP_URLPARAM => SANITIZE_ALLOW_ALL
140

141
142
        ];

Carsten  Rose's avatar
Carsten Rose committed
143
        self::$sanitizeStore = [
144
145
146
            STORE_FORM => true,
            STORE_SIP => false,
            STORE_RECORD => false,
147
            STORE_BEFORE => false,
148
            STORE_PARENT_RECORD => false,
149
150
            STORE_TABLE_DEFAULT => false,
            STORE_TABLE_COLUMN_TYPES => false,
151
152
            STORE_CLIENT => true,
            STORE_TYPO3 => false,
153
            STORE_VAR => false,
154
            STORE_ZERO => false,
155
            STORE_EMPTY => false,
156
            STORE_SYSTEM => false,
157
            STORE_EXTRA => false
158
159
        ];

160
        self::fillSystemStore($fileConfigIni);
161
        self::fillStoreTypo3($bodytext);
162
        self::fillStoreClient();
163
        self::fillStoreSip();
Carsten  Rose's avatar
Carsten Rose committed
164
        self::fillStoreExtra();
165

166
    }
167

168
    /**
169
     * Fill the system store by reading config.qfq.ini. Also setup config defaults.
Carsten  Rose's avatar
Carsten Rose committed
170
     *
171
     * @throws CodeException
172
     * @throws qfq\UserFormException
173
     */
174
    private static function fillSystemStore($fileConfigIni = '') {
175

176
        if ($fileConfigIni == '') {
177
            // Production Path to CONFIG_INI
178
179
180
181
            $fileConfigIni = __DIR__ . '/../../../../../' . CONFIG_INI;
            if (!file_exists($fileConfigIni)) {
                // PHPUnit Path to CONFIG_INI
                $fileConfigIni = __DIR__ . '/../../../' . CONFIG_INI;
182
183
184
            }
        }

185
        try {
186
            $config = parse_ini_file($fileConfigIni, false);
187

188
        } catch (\Exception $e) {
189
            throw new qfq\UserFormException ("Error read file " . $fileConfigIni . ": " . $e->getMessage(), ERROR_IO_READ_FILE);
190
        }
191

192
193
194
195
196
197
198
199
        $config = self::renameConfigElements($config);

        // Defaults
        Support::setIfNotSet($config, SYSTEM_DATE_FORMAT, 'yyyy-mm-dd');
        Support::setIfNotSet($config, SYSTEM_SHOW_DEBUG_INFO, 'auto');
        Support::setIfNotSet($config, F_BS_LABEL_COLUMNS, '3');
        Support::setIfNotSet($config, F_BS_INPUT_COLUMNS, '6');
        Support::setIfNotSet($config, F_BS_NOTE_COLUMNS, '3');
200
201
        Support::setIfNotSet($config, F_CLASS_PILL, 'qfq-color-grey-1');
        Support::setIfNotSet($config, F_CLASS_BODY, 'qfq-color-grey-2');
202
203
204


        $config = self::doSystemPath($config);
205
        $config = self::adjustConfig($config);
206

207
        self::checkMandatoryParameter($config);
208
209
210
211
212
213
214
215
216
217
218

        self::setVarArray($config, STORE_SYSTEM, true);
    }

    /**
     * Rename Elements defined in config.qfq.ini to more appropriate in user interaction.
     * E.g.: in config.qfq.ini everything is in upper case and word space is '_'. In Form.parameter it's lowercase and camel hook.
     *
     * @param array $config
     * @return array
     */
219
    private static function renameConfigElements(array $config) {
220
221
222
223
224
225

        // oldname > newname
        $setting = [
            [SYSTEM_FORM_BS_LABEL_COLUMNS, F_BS_LABEL_COLUMNS],
            [SYSTEM_FORM_BS_INPUT_COLUMNS, F_BS_INPUT_COLUMNS],
            [SYSTEM_FORM_BS_NOTE_COLUMNS, F_BS_NOTE_COLUMNS],
226
227
228
229
230
231
232
            [SYSTEM_FORM_DATA_PATTERN_ERROR, F_FE_DATA_PATTERN_ERROR],
            [SYSTEM_FORM_DATA_REQUIRED_ERROR, F_FE_DATA_REQUIRED_ERROR],
            [SYSTEM_FORM_DATA_MATCH_ERROR, F_FE_DATA_MATCH_ERROR],
            [SYSTEM_FORM_DATA_ERROR, F_FE_DATA_ERROR],
            [SYSTEM_CSS_CLASS_QFQ_FORM, F_CLASS],
            [SYSTEM_CSS_CLASS_QFQ_FORM_PILL, F_CLASS_PILL],
            [SYSTEM_CSS_CLASS_QFQ_FORM_BODY, F_CLASS_BODY],
233
234
235
236
237
238
239
240
241
242
243
244
        ];

        foreach ($setting as $row) {
            $oldName = $row[0];
            $newName = $row[1];

            if (isset($config[$oldName])) {
                $config[$newName] = $config[$oldName];
                if ($oldName != $newName) {
                    unset($config[$oldName]);
                }
            }
245
246
        }

247
248
249
250
        return $config;
    }

    /**
251
     * QFQ might be called via Typo3 (index.php) or directly via AJAX (directory: api). The
252
253
254
255
256
     * @param array $config
     * @return array
     */
    private function doSystemPath(array $config) {

257
258
259
260
261
262
263
        // SYSTEM_PATH_EXT: compute only if not already defined.
        if (!isset($config[SYSTEM_PATH_EXT]) || $config[SYSTEM_PATH_EXT] === '' || $config[SYSTEM_PATH_EXT][0] !== '/') {
            $relExtDir = '/typo3conf/ext/' . EXT_KEY;

            // If we are called through AJAX API (e.g. api/save.php), there is no TYPO3 environment.
            if (isset($_SERVER['SCRIPT_FILENAME'])) {
                $pos = strpos($_SERVER['SCRIPT_FILENAME'], $relExtDir);
264
265
                if ($pos === false && isset($GLOBALS['TYPO3_LOADED_EXT'][EXT_KEY]['ext_localconf.php'])) {

266
                    // Typo3 extension: probably index.php
267
                    $config[SYSTEM_PATH_EXT] = dirname($GLOBALS['TYPO3_LOADED_EXT'][EXT_KEY]['ext_localconf.php']);
268
                    $config[SYSTEM_SITE_PATH] = dirname($_SERVER['SCRIPT_FILENAME']);
269
                } else {
270
                    // API
271
                    $config[SYSTEM_PATH_EXT] = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos + strlen($relExtDir));
272
                    $config[SYSTEM_SITE_PATH] = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos);
273
                }
274
275
276
277
            } else {
                // No $_SERVER >>this means phpUnit.
                $config[SYSTEM_SITE_PATH] = getcwd();
                $config[SYSTEM_PATH_EXT] = getcwd();
278
279
            }
        }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

        return $config;
    }

    /**
     * Depending on some configuration value, update corresponding values.
     *
     * @param array $config
     * @return array
     */
    private static function adjustConfig(array $config) {
        // Adjust config
        if ($config[SYSTEM_SHOW_DEBUG_INFO] === 'auto') {
            $config[SYSTEM_SHOW_DEBUG_INFO] = (isset($GLOBALS["TSFE"]->beUserLogin) && $GLOBALS["TSFE"]->beUserLogin === true) ? 'yes' : 'no';
        }

        // make SQL PATH absolute. This is necessary to work in different directories correctly.
        if (isset($config[SYSTEM_SQL_LOG]) && $config[SYSTEM_SQL_LOG][0] !== '/') {
            $config[SYSTEM_SQL_LOG] = $config[SYSTEM_PATH_EXT] . '/' . $config[SYSTEM_SQL_LOG];
        }

301
        return $config;
302
303
    }

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
    /**
     * Iterate over all Parameter which have to exist in the config. Throw an array if any is missing.
     *
     * @param array $config
     * @throws UserFormException
     */
    private static function checkMandatoryParameter(array $config) {
        // Check mandatory config vars.
        $names = array('DB_USER', 'DB_SERVER', 'DB_PASSWORD', 'DB_NAME', 'SQL_LOG', 'SQL_LOG_MODE');
        foreach ($names as $name) {
            if (!isset($config[$name])) {
                throw new qfq\UserFormException ("Missing configuration in `config.ini`: $name", ERROR_MISSING_CONFIG_INI_VALUE);
            }
        }
    }

320
    /**
Carsten  Rose's avatar
Carsten Rose committed
321
322
     * Set or overwrite a complete store.
     *
323
324
     * @param array $dataArray
     * @param $store
325
     * @param bool|false $flagOverwrite
326
     * @throws UserFormException
327
     * @throws \qfq\CodeException
328
     */
329
    public static function setVarArray(array $dataArray, $store, $flagOverwrite = false) {
330

Carsten  Rose's avatar
Carsten Rose committed
331
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
332
        if (!isset(self::$sanitizeStore))
333
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
334

335
        if ($store === STORE_ZERO)
Carsten  Rose's avatar
Carsten Rose committed
336
            throw new CodeException("setVarArray() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO);
337

Carsten  Rose's avatar
Carsten Rose committed
338
        if (!$flagOverwrite && isset(self::$raw[$store]) && count(self::$raw[$store]) > 0) {
339
            throw new CodeException("Raw values already been copied to store '$store'. Do this only one time.", ERROR_STORE_VALUE_ALREADY_CODPIED);
340
        }
341

342
343
        self::$raw[$store] = $dataArray;
    }
344

345
    /**
346
347
348
     * Copy the BodyText as well as some T3 specific vars to STORE_TYPO3.
     * Attention: if called through API, there is no T3 environment. The only values which are available are fe_user and fe_user_uid.
     *
349
350
351
     * @param $bodytext
     * @throws CodeException
     */
352
    private static function fillStoreTypo3($bodytext) {
353

354
        // form=, showDebugBodyText=, 10.20..
355
        $arr = KeyValueStringParser::parse($bodytext, "=", "\n");
356

357
        if (isset($GLOBALS["TSFE"])) {
358

359
360
361
            if (isset($GLOBALS["TSFE"]->fe_user->user["username"])) {
                $arr[TYPO3_FE_USER] = $GLOBALS["TSFE"]->fe_user->user["username"];
            }
362

363
364
365
366
            if (isset($GLOBALS["TSFE"]->fe_user->user["uid"])) {
                $feUid = $GLOBALS["TSFE"]->fe_user->user["uid"];
                $arr[TYPO3_FE_USER_UID] = $GLOBALS["TSFE"]->fe_user->user["uid"];
            }
367

368
369
370
            if (isset($GLOBALS["TSFE"]->fe_user->user["usergroup"])) {
                $arr[TYPO3_FE_USER_GROUP] = $GLOBALS["TSFE"]->fe_user->user["usergroup"];
            }
Carsten  Rose's avatar
Carsten Rose committed
371

372
373
374
            if (isset($GLOBALS["TSFE"]->page["uid"])) {
                $arr[TYPO3_TT_CONTENT_UID] = $GLOBALS["TSFE"]->page["uid"];
            }
375

376
377
378
379
380
381
382
            if (isset($GLOBALS["TSFE"]->id)) {
                $arr[TYPO3_PAGE_ID] = $GLOBALS["TSFE"]->id;
            }

            if (isset($GLOBALS["TSFE"]->type)) {
                $arr[TYPO3_PAGE_TYPE] = $GLOBALS["TSFE"]->type;
            }
Carsten  Rose's avatar
Carsten Rose committed
383

384
385
386
            if (isset($GLOBALS["TSFE"]->sys_language_uid)) {
                $arr[TYPO3_PAGE_LANGUAGE] = $GLOBALS["TSFE"]->sys_language_uid;
            }
387

388
        } else {
389

390
            // NO T3 environment (called by API): restore from SESSION
391
392
393
394
            foreach([ SESSION_FE_USER, SESSION_FE_USER_UID, SESSION_FE_USER_GROUP ] as $key) {
                if (isset($_SESSION[SESSION_NAME][$key])) {
                    $arr[$key] = $_SESSION[SESSION_NAME][$key];
                }
395
396
            }
        }
397

398
        self::setVarArray($arr, STORE_TYPO3, true);
399
    }
400

401
    /**
Carsten  Rose's avatar
Carsten Rose committed
402
403
     * Fills the STORE_CLIENT
     *
404
405
     * @throws CodeException
     */
406
    private static function fillStoreClient() {
407
        // copy GET and POST and SERVER Parameter. Priority: SERVER, POST, GET
408
409
410
411
412
413
414
        $arr = array();
        if (isset($_GET))
            $arr = array_merge($arr, $_GET);

        if (isset($_POST))
            $arr = array_merge($arr, $_POST);

415
        // It's important to merge the SERVER array last: those entries shall overwrite client values.
416
417
        if (isset($_SERVER))
            $arr = array_merge($arr, $_SERVER);
418

419
        $arr = \qfq\Sanitize::normalize($arr);
420

421
        self::setVarArray($arr, STORE_CLIENT, true);
422

423
    }
424

Carsten  Rose's avatar
Carsten Rose committed
425
    /**
Carsten  Rose's avatar
Carsten Rose committed
426
427
     * Fills the STORE_SIP. Reads therefore specified SIP, decode the values and stores them in STORE_SIP.
     *
Carsten  Rose's avatar
Carsten Rose committed
428
     * @throws CodeException
429
     * @throws UserFormException
Carsten  Rose's avatar
Carsten Rose committed
430
     */
431
    private static function fillStoreSip() {
Carsten  Rose's avatar
Carsten Rose committed
432

433
        self::$sip = new Sip(self::$phpUnit);
434

435
436
437
438
        $s = self::getVar(CLIENT_SIP, STORE_CLIENT);
        if ($s !== false) {
            // if session is given, copy values to store
            $param = self::$sip->getVarsFromSip($s);
439
440
            $param[SIP_SIP] = $s;
            $param[SIP_URLPARAM] = self::$sip->getQueryStringFromSip($s);
441

442
//            self::setVarArray(KeyValueStringParser::parse($param, "=", "&"), STORE_SIP);
443
            self::setVarArray($param, STORE_SIP, true);
444
445
446
        }
    }

447
    /**
448
     * Cycles through all stores in $useStore.
449
     * First match will return the found value.
Carsten  Rose's avatar
Carsten Rose committed
450
     * During cycling: fill cache with requestet value and sanitize raw value.
451
     *
452
     * @param string $key
453
     * @param string $useStores f.e.: 'FSRD'
Carsten  Rose's avatar
Carsten Rose committed
454
     * @param string $sanitizeClass
Carsten  Rose's avatar
Carsten Rose committed
455
     * @param string $foundInStore Returns the name of the store where $key has been found. If $key is not found, return ''.
456
     * @return string a) if found: value, b) false
Carsten  Rose's avatar
Carsten Rose committed
457
     * @throws \qfq\CodeException
458
     */
Carsten  Rose's avatar
Carsten Rose committed
459
    public static function getVar($key, $useStores = STORE_USE_DEFAULT, $sanitizeClass = '', &$foundInStore = '') {
460
461

        // no store specifed?
462
        if ($useStores === "" || $useStores === null) {
463
            $useStores = STORE_USE_DEFAULT;
464
465
        }

466
        // no sanitizeClass specified: take predefined (if exist) or default.
467
        if ($sanitizeClass === '' || $sanitizeClass === null) {
Carsten  Rose's avatar
Carsten Rose committed
468
            $sanitizeClass = isset(self::$sanitizeClass[$key]) ? self::$sanitizeClass[$key] : SANITIZE_DEFAULT;
469
470
        }

471
472
473
        while ($useStores !== false) {

            $store = substr($useStores, 0, 1); // next store
Carsten  Rose's avatar
Carsten Rose committed
474
            $foundInStore = $store;
475
476
            $useStores = substr($useStores, 1); // shift left remaining stores

477
            if (!isset(self::$raw[$store][$key])) {
478
479
480
                switch ($store) {
                    case STORE_ZERO:
                        return 0;
481
482
                    case STORE_EMPTY:
                        return '';
483
484
                    case STORE_VAR:
                        if ($key === VAR_RANDOM) {
485
                            return Support::randomAlphaNum(RANDOM_LENGTH);
486
487
488
489
490
491
492
                        } else {
                            continue 2;  // no value provided, continue with while loop
                        }
                        break;
                    default:
                        continue 2; // no value provided, continue with while loop
                        break;
Carsten  Rose's avatar
Carsten Rose committed
493
                }
494
495
            }

496
            $rawVal = isset(self::$raw[$store][$key]) ? self::$raw[$store][$key] : null;
Carsten  Rose's avatar
Carsten Rose committed
497
            if (self::$sanitizeStore[$store] && $sanitizeClass != '') {
498
499
500
501
502
                if ($sanitizeClass == SANITIZE_ALLOW_PATTERN || $sanitizeClass == SANITIZE_ALLOW_MIN_MAX || $sanitizeClass == SANITIZE_ALLOW_MIN_MAX_DATE) {
                    // We do not have any pattern or min|max values at this point. For those who be affected, they already checked earlier. So set 'no check'
                    $sanitizeClass = SANITIZE_ALLOW_ALL;
                }
                return \qfq\Sanitize::sanitize($rawVal, $sanitizeClass, '', SANATIZE_EMPTY_STRING);
503
504
            } else {
                return $rawVal;
505
            }
506
        }
Carsten  Rose's avatar
Carsten Rose committed
507
        $foundInStore = '';
508
        return false;
509
    }
510

Carsten  Rose's avatar
Carsten Rose committed
511
    /**
Carsten  Rose's avatar
Carsten Rose committed
512
513
     * Fills the STORE_EXTRA.
     *
Carsten  Rose's avatar
Carsten Rose committed
514
515
516
517
     * @throws UserFormException
     * @throws \qfq\CodeException
     */
    private static function fillStoreExtra() {
518

519
        $value = Session::get(STORE_EXTRA);
520

521
        if (!isset($_SESSION[SESSION_NAME][STORE_EXTRA]) || $_SESSION[SESSION_NAME][STORE_EXTRA] === null) {
522
523
524
525
            $value = false;
        }

        if ($value === false) {
Carsten  Rose's avatar
Carsten Rose committed
526
            self::setVarArray(array(), STORE_EXTRA, true);
527
        } else {
528
            self::setVarArray($_SESSION[SESSION_NAME][STORE_EXTRA], STORE_EXTRA, true);
529
        }
Carsten  Rose's avatar
Carsten Rose committed
530
531
    }

532
    /**
Carsten  Rose's avatar
Carsten Rose committed
533
534
     * Returns a pointer to this Class.
     *
535
     * @param string $bodytext
536
     * @param bool|false $phpUnit
537
538
539
540
     * @param string $fileConfigIni
     * @return null|Store
     * @throws UserFormException
     * @throws \qfq\CodeException
541
     */
542
    public static function getInstance($bodytext = '', $phpUnit = false, $fileConfigIni = '') {
543

544
        if ($phpUnit) {
545

546
            if (self::$instance !== null) {
547
                // fake to have a clean environment for the next test.
548
549
550
551
552
                self::unsetStore(STORE_TYPO3);
                self::fillStoreTypo3($bodytext);

                self::unsetStore(STORE_CLIENT);
                self::fillStoreClient();
553
            }
554
555
556
557
558

            // Testing different config files means initialize completely
            if ($fileConfigIni != '') {
                self::$instance = null;
            }
559
560
561
562
        }

        // Design Pattern: Singleton
        if (self::$instance === null) {
563
564
            self::$phpUnit = $phpUnit;

565
            self::$instance = new self($bodytext, $fileConfigIni);
Carsten  Rose's avatar
Carsten Rose committed
566
567
568
569
        } else {
            // Class Store seems to be presistent over multiple QFQ instantiation. Set bodytext again, with every new request (if bodytext is given).
            if ($bodytext !== '')
                self::fillStoreTypo3($bodytext);
570
571
        }

572
573
574
575
576
577
578
        // Disable TYPO3_DEBUG_SHOW_BODY_TEXT=1 if SYSTEM_SHOW_DEBUG_INFO!='yes'
        if (self::getVar(TYPO3_DEBUG_SHOW_BODY_TEXT, STORE_TYPO3) === '1' &&
            self::getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM) !== 'yes'
        ) {
            self::setVar(TYPO3_DEBUG_SHOW_BODY_TEXT, '0', STORE_TYPO3);
        }

579
        return self::$instance;
580
    }
581

582
    /**
Carsten  Rose's avatar
Carsten Rose committed
583
584
     * Deletes a store assigning a new empty array to it.
     *
585
     * @param $store
586
587
     * @throws UserFormException
     * @throws \qfq\CodeException
588
589
     */
    public static function unsetStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
590
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
591
        if (!isset(self::$sanitizeStore))
592
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
593

594
        if ($store === STORE_ZERO)
Carsten  Rose's avatar
Carsten Rose committed
595
596
            throw new CodeException("unsetStore() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO);

597
598
599
        if (isset(self::$raw[$store])) {
            self::$raw[$store] = array();
        }
Carsten  Rose's avatar
Carsten Rose committed
600

601
602
    }

603
    /**
Carsten  Rose's avatar
Carsten Rose committed
604
605
     * Set's a single $key/$value pair $store.
     *
Carsten  Rose's avatar
Carsten Rose committed
606
607
608
     * @param string $key
     * @param string|array $value
     * @param string $store
609
610
611
612
613
614
615
616
617
618
619
620
621
     * @param bool|true $overWrite
     * @throws UserFormException
     * @throws \qfq\CodeException
     */
    public static function setVar($key, $value, $store, $overWrite = true) {
        // Check valid Storename
        if (!isset(self::$sanitizeStore))
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);

        if ($store === STORE_ZERO)
            throw new CodeException("setVar() for STORE_ZERO is impossible - there are no values.", ERROR_SET_STORE_ZERO);

        if ($overWrite === false && isset(self::$raw[$store][$key])) {
622
            throw new UserFormException("Value of '$key' already set in store '$store'.", ERROR_STORE_KEY_EXIST);
623
624
625
        }

        self::$raw[$store][$key] = $value;
Carsten  Rose's avatar
Carsten Rose committed
626
627
628

        // The STORE_EXTRA saves arrays and is persistent
        if ($store === STORE_EXTRA) {
629

630
            $store = Session::get(STORE_EXTRA);
631
632

            if ($store === false) {
633
                $store = array();
634
635
            }

636
637
            $store[$key] = $value;
            Session::set(STORE_EXTRA, $store);
638

Carsten  Rose's avatar
Carsten Rose committed
639
        }
640
641
    }

642
    /**
Carsten  Rose's avatar
Carsten Rose committed
643
644
     * Create a SIP after a form load. This is necessary on forms without a sip and on forms with r=0 (new record).
     *
645
646
647
     * @param $formName
     * @throws CodeException
     */
648
    public static function createSipAfterFormLoad($formName) {
649

650
        $recordId = self::getVar(CLIENT_RECORD_ID, STORE_TYPO3 . STORE_CLIENT);
651
652
653
654
        if ($recordId === false) {
            $recordId = 0;
        }

655
656
657
658
659
660
        // If there are existing SIP param, keep them by copying to the new SIP Param Array
        $tmpParam = self::getNonSystemSipParam();

        $tmpParam[SIP_RECORD_ID] = $recordId;
        $tmpParam[SIP_FORM] = $formName;

Carsten  Rose's avatar
#2067    
Carsten Rose committed
661
662
663
664
        if ($recordId == 0) {
            // SIPs for 'new records' needs to be uniq per TAB! Therefore add a uniq parameter
            $tmpParam[SIP_MAKE_URLPARAM_UNIQ] = uniqid();
        }
665
666

        // Construct fake urlparam
667
        $tmpUrlparam = OnArray::toString($tmpParam);
668
669

        // Create a fake SIP which has never been passed by URL - further processing might expect this to exist.
670
        $sip = self::getSipInstance()->queryStringToSip($tmpUrlparam, RETURN_SIP);
671
        self::setVar(CLIENT_SIP, $sip, STORE_CLIENT);
672
673
674

        // Store in SIP Store (cause it's empty until now).
        $tmpParam[SIP_SIP] = $sip;
675
        self::setVarArray($tmpParam, STORE_SIP, true);
676

677
678
679
    }

    /**
680
     * Return an array with non system SIP parameter. Take the whole STORE_SIP and search for non system parameter.
681
     *
682
683
684
     * @return array
     * @throws UserFormException
     * @throws \qfq\CodeException
685
     */
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
    private static function getNonSystemSipParam() {
        $tmpParam = array();

        $sipArray = self::getStore(STORE_SIP);

        foreach ($sipArray as $key => $value) {
            if ($key[0] === '_') {
                continue;
            }
            switch ($key) {
                case SIP_SIP:
                case SIP_RECORD_ID:
                case SIP_FORM;
                case SIP_URLPARAM:
                    continue;
                default:
                    $tmpParam[$key] = $value;
            }
        }

        return $tmpParam;
707
    }
708

709
    /**
Carsten  Rose's avatar
Carsten Rose committed
710
711
     * Returns a complete $store.
     *
712
     * @param $store
713
714
715
     * @return array
     * @throws UserFormException
     * @throws \qfq\CodeException
716
717
     */
    public static function getStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
718
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
719
        if (!isset(self::$sanitizeStore[$store]))
720
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
721

722
        if ($store === STORE_ZERO)
Carsten  Rose's avatar
Carsten Rose committed
723
724
            throw new CodeException("getStore() for STORE_ZERO is impossible - there are no values saved.", ERROR_GET_STORE_ZERO);

725
726
727
        if (isset(self::$raw[$store])) {
            return self::$raw[$store];
        }
728

729
730
        return array();
    }
731

732
733
734
735
736
737
738
739
    /**
     * Returns a pointer to this class.
     *
     * @return null|Sip
     */
    public static function getSipInstance() {
        return self::$sip;
    }
Carsten  Rose's avatar
Carsten Rose committed
740

741
    /**
Carsten  Rose's avatar
Carsten Rose committed
742
743
     * Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
     *
744
745
746
     * @param $tableName
     * @throws CodeException
     */
747
    public static function fillStoreTableDefaultColumnType($tableName) {
748
749
750
751
        $db = new qfq\Database();

        $tableDefinition = $db->getTableDefinition($tableName);

Carsten  Rose's avatar
Carsten Rose committed
752
753
        self::setVarArray(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT, true);
        self::setVarArray(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES, true);
754
    }
755
756
757
758
759
}