Store.php 23.9 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 = '') {
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();
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
    /**
Carsten  Rose's avatar
Carsten Rose committed
170
171
     * Fills the system store.
     *
172
     * @throws CodeException
173
     * @throws qfq\UserFormException
174
     */
175
    private static function fillSystemStore() {
176
177
178
179
180
181
182
183
184
185
186
187

        // PHPUnit Path to CONFIG_INI
        $configIni = __DIR__ . '/../../../' . CONFIG_INI;
        if (!file_exists($configIni)) {
            // Production Path to CONFIG_INI
            $configIni = __DIR__ . '/../../../../../' . CONFIG_INI;

            if (!file_exists($configIni)) {
                throw new qfq\UserFormException ("Config not found: " . getcwd() . "/" . $configIni, ERROR_IO_READ_FILE);
            }
        }

188
        try {
189
            $config = parse_ini_file($configIni, false);
190

191
        } catch (\Exception $e) {
192
            throw new qfq\UserFormException ("Error read file " . CONFIG_INI . ": " . $e->getMessage(), ERROR_IO_READ_FILE);
193
        }
194

195
196
197
198
199
200
201
202
203
        $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');

204
        // Adjust config
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
        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];
        }

        // Verify existence
        $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
     */
    private function renameConfigElements(array $config) {

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

255
256
257
258
259
260
261
262
263
        return $config;
    }

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

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

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

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

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

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

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

312
313
        self::$raw[$store] = $dataArray;
    }
314

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

324
        // form=, showDebugBodyText=, 10.20..
325
        $arr = KeyValueStringParser::parse($bodytext, "=", "\n");
326

327
        if (isset($GLOBALS["TSFE"])) {
328

329
330
331
            if (isset($GLOBALS["TSFE"]->fe_user->user["username"])) {
                $arr[TYPO3_FE_USER] = $GLOBALS["TSFE"]->fe_user->user["username"];
            }
332

333
334
335
336
            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"];
            }
337

338
339
340
            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
341

342
343
344
            if (isset($GLOBALS["TSFE"]->page["uid"])) {
                $arr[TYPO3_TT_CONTENT_UID] = $GLOBALS["TSFE"]->page["uid"];
            }
345

346
347
348
349
350
351
352
            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
353

354
355
356
            if (isset($GLOBALS["TSFE"]->sys_language_uid)) {
                $arr[TYPO3_PAGE_LANGUAGE] = $GLOBALS["TSFE"]->sys_language_uid;
            }
357

358
        } else {
359

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

368
        self::setVarArray($arr, STORE_TYPO3, true);
369
    }
370

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

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

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

389
        $arr = \qfq\Sanitize::normalizeArray($arr);
390

391
        self::setVarArray($arr, STORE_CLIENT, true);
392
    }
393

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

402
        self::$sip = new Sip(self::$phpUnit);
403

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

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

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

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

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

440
441
442
        while ($useStores !== false) {

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

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

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

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

488
        $value = Session::get(STORE_EXTRA);
489

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

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

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

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

                self::unsetStore(STORE_CLIENT);
                self::fillStoreClient();
518
519
520
521
522
            }
        }

        // Design Pattern: Singleton
        if (self::$instance === null) {
523
524
525
            self::$phpUnit = $phpUnit;

            self::$instance = new self($bodytext);
Carsten  Rose's avatar
Carsten Rose committed
526
527
528
529
        } 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);
530
531
        }

532
533
534
535
536
537
538
        // 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);
        }

539
        return self::$instance;
540
    }
541

542
    /**
Carsten  Rose's avatar
Carsten Rose committed
543
544
     * Deletes a store assigning a new empty array to it.
     *
545
     * @param $store
546
547
     * @throws UserFormException
     * @throws \qfq\CodeException
548
549
     */
    public static function unsetStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
550
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
551
        if (!isset(self::$sanitizeStore))
552
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
553

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

557
558
559
        if (isset(self::$raw[$store])) {
            self::$raw[$store] = array();
        }
Carsten  Rose's avatar
Carsten Rose committed
560

561
562
    }

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

        self::$raw[$store][$key] = $value;
Carsten  Rose's avatar
Carsten Rose committed
586
587
588

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

590
            $store = Session::get(STORE_EXTRA);
591
592

            if ($store === false) {
593
                $store = array();
594
595
            }

596
597
            $store[$key] = $value;
            Session::set(STORE_EXTRA, $store);
598

Carsten  Rose's avatar
Carsten Rose committed
599
        }
600
601
    }

602
    /**
Carsten  Rose's avatar
Carsten Rose committed
603
604
     * Create a SIP after a form load. This is necessary on forms without a sip and on forms with r=0 (new record).
     *
605
606
607
     * @param $formName
     * @throws CodeException
     */
608
    public static function createSipAfterFormLoad($formName) {
609

610
        $recordId = self::getVar(CLIENT_RECORD_ID, STORE_TYPO3 . STORE_CLIENT);
611
612
613
614
        if ($recordId === false) {
            $recordId = 0;
        }

615
616
617
618
619
620
        // 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
621
622
623
624
        if ($recordId == 0) {
            // SIPs for 'new records' needs to be uniq per TAB! Therefore add a uniq parameter
            $tmpParam[SIP_MAKE_URLPARAM_UNIQ] = uniqid();
        }
625
626

        // Construct fake urlparam
627
        $tmpUrlparam = OnArray::toString($tmpParam);
628
629

        // Create a fake SIP which has never been passed by URL - further processing might expect this to exist.
630
        $sip = self::getSipInstance()->queryStringToSip($tmpUrlparam, RETURN_SIP);
631
        self::setVar(CLIENT_SIP, $sip, STORE_CLIENT);
632
633
634

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

637
638
639
    }

    /**
640
641
642
643
     * 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
644
     */
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
    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;
666
    }
667

668
    /**
Carsten  Rose's avatar
Carsten Rose committed
669
670
     * Returns a complete $store.
     *
671
     * @param $store
672
673
674
     * @return array
     * @throws UserFormException
     * @throws \qfq\CodeException
675
676
     */
    public static function getStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
677
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
678
        if (!isset(self::$sanitizeStore[$store]))
679
            throw new UserFormException("Unknown Store: $store", ERROR_UNNOWN_STORE);
Carsten  Rose's avatar
Carsten Rose committed
680

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

684
685
686
        if (isset(self::$raw[$store])) {
            return self::$raw[$store];
        }
687

688
689
        return array();
    }
690

691
692
693
694
695
696
697
698
    /**
     * Returns a pointer to this class.
     *
     * @return null|Sip
     */
    public static function getSipInstance() {
        return self::$sip;
    }
Carsten  Rose's avatar
Carsten Rose committed
699

700
    /**
Carsten  Rose's avatar
Carsten Rose committed
701
702
     * Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
     *
703
704
705
     * @param $tableName
     * @throws CodeException
     */
706
    public static function fillStoreTableDefaultColumnType($tableName) {
707
708
709
710
        $db = new qfq\Database();

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

Carsten  Rose's avatar
Carsten Rose committed
711
712
        self::setVarArray(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT, true);
        self::setVarArray(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES, true);
713
    }
714
715
716
717
718
}