Store.php 25.6 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
        Support::setIfNotSet($config, F_BUTTON_ON_CHANGE_CLASS, 'btn-info alert-info');
203
204
205


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

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

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

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

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

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

249
250
251
252
        return $config;
    }

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

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

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

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

303
        return $config;
304
305
    }

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

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

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

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

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

344
345
        self::$raw[$store] = $dataArray;
    }
346

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

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

359
        if (isset($GLOBALS["TSFE"])) {
360

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

365
366
367
368
            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"];
            }
369

370
371
372
            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
373

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

378
379
380
381
382
383
384
            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
385

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

390
        } else {
391

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

400
        self::setVarArray($arr, STORE_TYPO3, true);
401
    }
402

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

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

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

421
        $arr = \qfq\Sanitize::normalize($arr);
422

423
        self::setVarArray($arr, STORE_CLIENT, true);
424

425
    }
426

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

435
        self::$sip = new Sip(self::$phpUnit);
436

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

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

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

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

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

473
474
475
        while ($useStores !== false) {

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

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

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

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

521
        $value = Session::get(STORE_EXTRA);
522

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

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

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

546
        if ($phpUnit) {
547

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

                self::unsetStore(STORE_CLIENT);
                self::fillStoreClient();
555
            }
556
557
558
559
560

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

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

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

574
575
576
577
578
579
580
        // 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);
        }

581
        return self::$instance;
582
    }
583

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

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

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

603
604
    }

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

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

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

632
            $store = Session::get(STORE_EXTRA);
633
634

            if ($store === false) {
635
                $store = array();
636
637
            }

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

Carsten  Rose's avatar
Carsten Rose committed
641
        }
642
643
    }

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

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

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

        // Construct fake urlparam
669
        $tmpUrlparam = OnArray::toString($tmpParam);
670
671

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

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

679
680
681
    }

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

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

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

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

731
732
        return array();
    }
733

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

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

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

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