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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
        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,

//            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
139

140
141
        ];

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

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

165
    }
166

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

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

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

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

191
192
193
194
195
196
197
198
        $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');
199
200
        Support::setIfNotSet($config, F_CLASS_PILL, 'qfq-color-grey-1');
        Support::setIfNotSet($config, F_CLASS_BODY, 'qfq-color-grey-2');
201
202
203


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

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

        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
     */
218
    private static function renameConfigElements(array $config) {
219
220
221
222
223
224

        // 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],
225
226
227
228
229
230
231
            [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],
232
233
234
235
236
237
238
239
240
241
242
243
        ];

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

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

246
247
248
249
        return $config;
    }

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

256
257
258
259
260
261
262
        // 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);
263
264
                if ($pos === false && isset($GLOBALS['TYPO3_LOADED_EXT'][EXT_KEY]['ext_localconf.php'])) {

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

        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];
        }

300
        return $config;
301
302
    }

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
    /**
     * 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);
            }
        }
    }

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

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

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

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

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

344
    /**
345
346
347
     * 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.
     *
348
349
350
     * @param $bodytext
     * @throws CodeException
     */
351
    private static function fillStoreTypo3($bodytext) {
352

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

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

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

362
363
364
365
            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"];
            }
366

367
368
369
            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
370

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

375
376
377
378
379
380
381
            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
382

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

387
        } else {
388

389
            // NO T3 environment (called by API): restore from SESSION
390
391
392
393
            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];
                }
394
395
            }
        }
396

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

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

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

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

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

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

422
    }
423

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

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

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

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

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

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

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

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

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

476
            if (!isset(self::$raw[$store][$key])) {
477
478
479
                switch ($store) {
                    case STORE_ZERO:
                        return 0;
480
481
                    case STORE_EMPTY:
                        return '';
482
483
                    case STORE_VAR:
                        if ($key === VAR_RANDOM) {
484
                            return Support::randomAlphaNum(RANDOM_LENGTH);
485
486
487
488
489
490
491
                        } 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
492
                }
493
494
            }

495
            $rawVal = isset(self::$raw[$store][$key]) ? self::$raw[$store][$key] : null;
Carsten  Rose's avatar
Carsten Rose committed
496
            if (self::$sanitizeStore[$store] && $sanitizeClass != '') {
497
498
499
500
501
                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);
502
503
            } else {
                return $rawVal;
504
            }
505
        }
Carsten  Rose's avatar
Carsten Rose committed
506
        $foundInStore = '';
507
        return false;
508
    }
509

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

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

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

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

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

543
        if ($phpUnit) {
544

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

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

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

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

564
            self::$instance = new self($bodytext, $fileConfigIni);
Carsten  Rose's avatar
Carsten Rose committed
565
566
567
568
        } 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);
569
570
        }

571
572
573
574
575
576
577
        // 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);
        }

578
        return self::$instance;
579
    }
580

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

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

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

600
601
    }

602
    /**
Carsten  Rose's avatar
Carsten Rose committed
603
604
     * Set's a single $key/$value pair $store.
     *
Carsten  Rose's avatar
Carsten Rose committed
605
606
607
     * @param string $key
     * @param string|array $value
     * @param string $store
608
609
610
611
612
613
614
615
616
617
618
619
620
     * @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])) {
621
            throw new UserFormException("Value of '$key' already set in store '$store'.", ERROR_STORE_KEY_EXIST);
622
623
624
        }

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

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

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

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

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

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

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

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

654
655
656
657
658
659
        // 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
660
661
662
663
        if ($recordId == 0) {
            // SIPs for 'new records' needs to be uniq per TAB! Therefore add a uniq parameter
            $tmpParam[SIP_MAKE_URLPARAM_UNIQ] = uniqid();
        }
664
665

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

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

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

676
677
678
    }

    /**
679
     * Return an array with non system SIP parameter. Take the whole STORE_SIP and search for non system parameter.
680
     *
681
682
683
     * @return array
     * @throws UserFormException
     * @throws \qfq\CodeException
684
     */
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
    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;
706
    }
707

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

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

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

728
729
        return array();
    }
730

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

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

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

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