Store.php 24.1 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
    /**
Carsten  Rose's avatar
Carsten Rose committed
169
170
     * Fills the system store.
     *
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
200
        $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');

201
        // Adjust config
202
203
204
205
206
207
208
209
210
211
212
        if ($config[SYSTEM_SHOW_DEBUG_INFO] === 'auto') {
            $config[SYSTEM_SHOW_DEBUG_INFO] = (isset($GLOBALS["TSFE"]->beUserLogin) && $GLOBALS["TSFE"]->beUserLogin === true) ? 'yes' : 'no';
        }

        $config = self::doSystemPath($config);

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

213
        // Check mandatory config vars.
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
        $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);
            }
        }

        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
     */
231
    private static function renameConfigElements(array $config) {
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

        // 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],
        ];

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

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

252
253
254
255
256
257
258
259
260
        return $config;
    }

    /**
     * @param array $config
     * @return array
     */
    private function doSystemPath(array $config) {

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

270
                    // Typo3 extension: probably index.php
271
                    $config[SYSTEM_PATH_EXT] = dirname($GLOBALS['TYPO3_LOADED_EXT'][EXT_KEY]['ext_localconf.php']);
272
                    $config[SYSTEM_SITE_PATH] = dirname($_SERVER['SCRIPT_FILENAME']);
273
                } else {
274
                    // API
275
                    $config[SYSTEM_PATH_EXT] = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos + strlen($relExtDir));
276
                    $config[SYSTEM_SITE_PATH] = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos);
277
                }
278
279
280
281
            } else {
                // No $_SERVER >>this means phpUnit.
                $config[SYSTEM_SITE_PATH] = getcwd();
                $config[SYSTEM_PATH_EXT] = getcwd();
282
283
            }
        }
284
        return $config;
285
286
    }

287
    /**
Carsten  Rose's avatar
Carsten Rose committed
288
289
     * Set or overwrite a complete store.
     *
290
291
     * @param array $dataArray
     * @param $store
292
     * @param bool|false $flagOverwrite
293
     * @throws UserFormException
294
     * @throws \qfq\CodeException
295
     */
296
    public static function setVarArray(array $dataArray, $store, $flagOverwrite = false) {
297

Carsten  Rose's avatar
Carsten Rose committed
298
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
299
        if (!isset(self::$sanitizeStore))
300
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
301

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

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

309
310
        self::$raw[$store] = $dataArray;
    }
311

312
    /**
313
314
315
     * 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.
     *
316
317
318
     * @param $bodytext
     * @throws CodeException
     */
319
    private static function fillStoreTypo3($bodytext) {
320

321
        // form=, showDebugBodyText=, 10.20..
322
        $arr = KeyValueStringParser::parse($bodytext, "=", "\n");
323

324
        if (isset($GLOBALS["TSFE"])) {
325

326
327
328
            if (isset($GLOBALS["TSFE"]->fe_user->user["username"])) {
                $arr[TYPO3_FE_USER] = $GLOBALS["TSFE"]->fe_user->user["username"];
            }
329

330
331
332
333
            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"];
            }
334

335
336
337
            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
338

339
340
341
            if (isset($GLOBALS["TSFE"]->page["uid"])) {
                $arr[TYPO3_TT_CONTENT_UID] = $GLOBALS["TSFE"]->page["uid"];
            }
342

343
344
345
346
347
348
349
            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
350

351
352
353
            if (isset($GLOBALS["TSFE"]->sys_language_uid)) {
                $arr[TYPO3_PAGE_LANGUAGE] = $GLOBALS["TSFE"]->sys_language_uid;
            }
354

355
        } else {
356

357
            // NO T3 environment (called by API): restore from SESSION
358
359
360
361
            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];
                }
362
363
            }
        }
364

365
        self::setVarArray($arr, STORE_TYPO3, true);
366
    }
367

368
    /**
Carsten  Rose's avatar
Carsten Rose committed
369
370
     * Fills the STORE_CLIENT
     *
371
372
     * @throws CodeException
     */
373
    private static function fillStoreClient() {
374
        // copy GET and POST and SERVER Parameter. Priority: SERVER, POST, GET
375
376
377
378
379
380
381
        $arr = array();
        if (isset($_GET))
            $arr = array_merge($arr, $_GET);

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

382
        // It's important to merge the SERVER array last: those entries shall overwrite client values.
383
384
        if (isset($_SERVER))
            $arr = array_merge($arr, $_SERVER);
385

386
        $arr = \qfq\Sanitize::normalize($arr);
387

388
        self::setVarArray($arr, STORE_CLIENT, true);
389

390
    }
391

Carsten  Rose's avatar
Carsten Rose committed
392
    /**
Carsten  Rose's avatar
Carsten Rose committed
393
394
     * Fills the STORE_SIP. Reads therefore specified SIP, decode the values and stores them in STORE_SIP.
     *
Carsten  Rose's avatar
Carsten Rose committed
395
     * @throws CodeException
396
     * @throws UserFormException
Carsten  Rose's avatar
Carsten Rose committed
397
     */
398
    private static function fillStoreSip() {
Carsten  Rose's avatar
Carsten Rose committed
399

400
        self::$sip = new Sip(self::$phpUnit);
401

402
403
404
405
        $s = self::getVar(CLIENT_SIP, STORE_CLIENT);
        if ($s !== false) {
            // if session is given, copy values to store
            $param = self::$sip->getVarsFromSip($s);
406
407
            $param[SIP_SIP] = $s;
            $param[SIP_URLPARAM] = self::$sip->getQueryStringFromSip($s);
408

409
//            self::setVarArray(KeyValueStringParser::parse($param, "=", "&"), STORE_SIP);
410
            self::setVarArray($param, STORE_SIP, true);
411
412
413
        }
    }

414
    /**
415
     * Cycles through all stores in $useStore.
416
     * First match will return the found value.
Carsten  Rose's avatar
Carsten Rose committed
417
     * During cycling: fill cache with requestet value and sanitize raw value.
418
     *
419
     * @param string $key
420
     * @param string $useStores f.e.: 'FSRD'
Carsten  Rose's avatar
Carsten Rose committed
421
     * @param string $sanitizeClass
Carsten  Rose's avatar
Carsten Rose committed
422
     * @param string $foundInStore Returns the name of the store where $key has been found. If $key is not found, return ''.
423
     * @return string a) if found: value, b) false
Carsten  Rose's avatar
Carsten Rose committed
424
     * @throws \qfq\CodeException
425
     */
Carsten  Rose's avatar
Carsten Rose committed
426
    public static function getVar($key, $useStores = STORE_USE_DEFAULT, $sanitizeClass = '', &$foundInStore = '') {
427
428

        // no store specifed?
429
        if ($useStores === "" || $useStores === null) {
430
            $useStores = STORE_USE_DEFAULT;
431
432
        }

433
        // no sanitizeClass specified: take predefined (if exist) or default.
434
        if ($sanitizeClass === '' || $sanitizeClass === null) {
Carsten  Rose's avatar
Carsten Rose committed
435
            $sanitizeClass = isset(self::$sanitizeClass[$key]) ? self::$sanitizeClass[$key] : SANITIZE_DEFAULT;
436
437
        }

438
439
440
        while ($useStores !== false) {

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

444
            if (!isset(self::$raw[$store][$key])) {
445
446
447
                switch ($store) {
                    case STORE_ZERO:
                        return 0;
448
449
                    case STORE_EMPTY:
                        return '';
450
451
                    case STORE_VAR:
                        if ($key === VAR_RANDOM) {
452
                            return Support::randomAlphaNum(RANDOM_LENGTH);
453
454
455
456
457
458
459
                        } 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
460
                }
461
462
            }

463
            $rawVal = isset(self::$raw[$store][$key]) ? self::$raw[$store][$key] : null;
Carsten  Rose's avatar
Carsten Rose committed
464
            if (self::$sanitizeStore[$store] && $sanitizeClass != '') {
465
466
467
468
469
                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);
470
471
            } else {
                return $rawVal;
472
            }
473
        }
Carsten  Rose's avatar
Carsten Rose committed
474
        $foundInStore = '';
475
        return false;
476
    }
477

Carsten  Rose's avatar
Carsten Rose committed
478
    /**
Carsten  Rose's avatar
Carsten Rose committed
479
480
     * Fills the STORE_EXTRA.
     *
Carsten  Rose's avatar
Carsten Rose committed
481
482
483
484
     * @throws UserFormException
     * @throws \qfq\CodeException
     */
    private static function fillStoreExtra() {
485

486
        $value = Session::get(STORE_EXTRA);
487

488
        if (!isset($_SESSION[SESSION_NAME][STORE_EXTRA]) || $_SESSION[SESSION_NAME][STORE_EXTRA] === null) {
489
490
491
492
            $value = false;
        }

        if ($value === false) {
Carsten  Rose's avatar
Carsten Rose committed
493
            self::setVarArray(array(), STORE_EXTRA, true);
494
        } else {
495
            self::setVarArray($_SESSION[SESSION_NAME][STORE_EXTRA], STORE_EXTRA, true);
496
        }
Carsten  Rose's avatar
Carsten Rose committed
497
498
    }

499
    /**
Carsten  Rose's avatar
Carsten Rose committed
500
501
     * Returns a pointer to this Class.
     *
502
     * @param string $bodytext
503
     * @param bool|false $phpUnit
504
505
506
507
     * @param string $fileConfigIni
     * @return null|Store
     * @throws UserFormException
     * @throws \qfq\CodeException
508
     */
509
    public static function getInstance($bodytext = '', $phpUnit = false, $fileConfigIni = '') {
510

511
        if ($phpUnit) {
512

513
            if (self::$instance !== null) {
514
                // fake to have a clean environment for the next test.
515
516
517
518
519
                self::unsetStore(STORE_TYPO3);
                self::fillStoreTypo3($bodytext);

                self::unsetStore(STORE_CLIENT);
                self::fillStoreClient();
520
            }
521
522
523
524
525

            // Testing different config files means initialize completely
            if ($fileConfigIni != '') {
                self::$instance = null;
            }
526
527
528
529
        }

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

532
            self::$instance = new self($bodytext, $fileConfigIni);
Carsten  Rose's avatar
Carsten Rose committed
533
534
535
536
        } 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);
537
538
        }

539
540
541
542
543
544
545
        // 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);
        }

546
        return self::$instance;
547
    }
548

549
    /**
Carsten  Rose's avatar
Carsten Rose committed
550
551
     * Deletes a store assigning a new empty array to it.
     *
552
     * @param $store
553
554
     * @throws UserFormException
     * @throws \qfq\CodeException
555
556
     */
    public static function unsetStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
557
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
558
        if (!isset(self::$sanitizeStore))
559
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
560

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

564
565
566
        if (isset(self::$raw[$store])) {
            self::$raw[$store] = array();
        }
Carsten  Rose's avatar
Carsten Rose committed
567

568
569
    }

570
    /**
Carsten  Rose's avatar
Carsten Rose committed
571
572
     * Set's a single $key/$value pair $store.
     *
Carsten  Rose's avatar
Carsten Rose committed
573
574
575
     * @param string $key
     * @param string|array $value
     * @param string $store
576
577
578
579
580
581
582
583
584
585
586
587
588
     * @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])) {
589
            throw new UserFormException("Value of '$key' already set in store '$store'.", ERROR_STORE_KEY_EXIST);
590
591
592
        }

        self::$raw[$store][$key] = $value;
Carsten  Rose's avatar
Carsten Rose committed
593
594
595

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

597
            $store = Session::get(STORE_EXTRA);
598
599

            if ($store === false) {
600
                $store = array();
601
602
            }

603
604
            $store[$key] = $value;
            Session::set(STORE_EXTRA, $store);
605

Carsten  Rose's avatar
Carsten Rose committed
606
        }
607
608
    }

609
    /**
Carsten  Rose's avatar
Carsten Rose committed
610
611
     * Create a SIP after a form load. This is necessary on forms without a sip and on forms with r=0 (new record).
     *
612
613
614
     * @param $formName
     * @throws CodeException
     */
615
    public static function createSipAfterFormLoad($formName) {
616

617
        $recordId = self::getVar(CLIENT_RECORD_ID, STORE_TYPO3 . STORE_CLIENT);
618
619
620
621
        if ($recordId === false) {
            $recordId = 0;
        }

622
623
624
625
626
627
        // 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
628
629
630
631
        if ($recordId == 0) {
            // SIPs for 'new records' needs to be uniq per TAB! Therefore add a uniq parameter
            $tmpParam[SIP_MAKE_URLPARAM_UNIQ] = uniqid();
        }
632
633

        // Construct fake urlparam
634
        $tmpUrlparam = OnArray::toString($tmpParam);
635
636

        // Create a fake SIP which has never been passed by URL - further processing might expect this to exist.
637
        $sip = self::getSipInstance()->queryStringToSip($tmpUrlparam, RETURN_SIP);
638
        self::setVar(CLIENT_SIP, $sip, STORE_CLIENT);
639
640
641

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

644
645
646
    }

    /**
647
648
649
650
     * Return an array with non system SIP parameter. Take the whole STORE_SIP and search for non system parameter.
     * @return array
     * @throws UserFormException
     * @throws \qfq\CodeException
651
     */
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
    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;
673
    }
674

675
    /**
Carsten  Rose's avatar
Carsten Rose committed
676
677
     * Returns a complete $store.
     *
678
     * @param $store
679
680
681
     * @return array
     * @throws UserFormException
     * @throws \qfq\CodeException
682
683
     */
    public static function getStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
684
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
685
        if (!isset(self::$sanitizeStore[$store]))
686
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
687

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

691
692
693
        if (isset(self::$raw[$store])) {
            return self::$raw[$store];
        }
694

695
696
        return array();
    }
697

698
699
700
701
702
703
704
705
    /**
     * Returns a pointer to this class.
     *
     * @return null|Sip
     */
    public static function getSipInstance() {
        return self::$sip;
    }
Carsten  Rose's avatar
Carsten Rose committed
706

707
    /**
Carsten  Rose's avatar
Carsten Rose committed
708
709
     * Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
     *
710
711
712
     * @param $tableName
     * @throws CodeException
     */
713
    public static function fillStoreTableDefaultColumnType($tableName) {
714
715
716
717
        $db = new qfq\Database();

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

Carsten  Rose's avatar
Carsten Rose committed
718
719
        self::setVarArray(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT, true);
        self::setVarArray(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES, true);
720
    }
721
722
723
724
725
}