Store.php 14.3 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/Database.php');
21
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;

Carsten  Rose's avatar
Carsten Rose committed
48
49
50
51
52
53
54
55
56
57
    /**
     * @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
     */
58
    private static $raw = array();
Carsten  Rose's avatar
Carsten Rose committed
59
60

    /**
Carsten  Rose's avatar
Carsten Rose committed
61
     * @var array Default sanitize classes.
Carsten  Rose's avatar
Carsten Rose committed
62
     */
Carsten  Rose's avatar
Carsten Rose committed
63
    private static $sanitizeClass = array();
Carsten  Rose's avatar
Carsten Rose committed
64
65

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

    /**
75
     * @param string $bodytext
76
     */
77
78
    private function __construct($bodytext = '') {

Carsten  Rose's avatar
Carsten Rose committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
        self::$sanitizeClass = [
//            TYPO3_DEBUG_LOAD => SANITIZE_ALLOW_DIGIT,
//            TYPO3_DEBUG_SAVE => SANITIZE_ALLOW_DIGIT,
//            TYPO3_FORM => SANITIZE_ALLOW_ALNUMX,
//            TYPO3_FE_USER => SANITIZE_ALLOW_ALNUMX,
//            TYPO3_FE_USER_UID => SANITIZE_ALLOW_DIGIT,
//            TYPO3_FE_USER_GROUP => SANITIZE_ALLOW_ALNUMX,

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

            // Part of $_SERVER. Missing vars must be requested individual with the needed sanitize class.
            CLIENT_SCRIPT_URL => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SCRIPT_URI => SANITIZE_ALLOW_ALNUMX,
            CLIENT_HTTP_HOST => SANITIZE_ALLOW_ALNUMX,
            CLIENT_HTTP_USER_AGENT => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SERVER_NAME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SERVER_ADDRESS => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SERVER_PORT => SANITIZE_ALLOW_DIGIT,
            CLIENT_REMOTE_ADDRESS => SANITIZE_ALLOW_ALNUMX,
            CLIENT_REQUEST_SCHEME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_SCRIPT_FILENAME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_QUERY_STRING => SANITIZE_ALLOW_ALL,
            CLIENT_REQUEST_URI => SANITIZE_ALLOW_ALL,
            CLIENT_SCRIPT_NAME => SANITIZE_ALLOW_ALNUMX,
            CLIENT_PHP_SELF => SANITIZE_ALLOW_ALNUMX,

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

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

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

130
131
        ];

Carsten  Rose's avatar
Carsten Rose committed
132
        self::$sanitizeStore = [
133
134
135
136
            STORE_FORM => true,
            STORE_SIP => false,
            STORE_RECORD => false,
            STORE_PARENT_RECORD => false,
137
138
            STORE_TABLE_DEFAULT => false,
            STORE_TABLE_COLUMN_TYPES => false,
139
140
            STORE_CLIENT => true,
            STORE_TYPO3 => false,
141
            STORE_ZERO => false,
142
143
144
            STORE_SYSTEM => false
        ];

145
        self::fillSystemStore();
146
        self::fillStoreTypo3($bodytext);
147
        self::fillStoreClient();
148
        self::fillStoreSip();
149
    }
150

151
152
    /**
     * @throws CodeException
153
     * @throws qfq\UserException
154
155
     */
    private function fillSystemStore() {
156
        try {
157
158
            //TODO: Vernuenftige Fehlermeldung falls nicht auf qfq.ini zugegriffen werden kann.
            //TODO: sinnvollen Platz fuer qfq.ini bestimmen. In der Installationsdoku erwaehnen.
159
            $config = parse_ini_file(__DIR__ . '/../../../' . CONFIG_INI, false);
160

161
162
            //TODO: auskommentiert weil dann die Unittests nicht mehr laufen. Sollte eigentlich wieder aktiviert werden.
//            $config['SQLLOG'] = Support::ifRelativePathPrependExtensionPath($config['SQLLOG']);
163

164
        } catch (\Exception $e) {
165
            throw new qfq\UserException ("Error read file " . CONFIG_INI . ": " . $e->getMessage(), ERROR_IO_READ_FILE);
166
        }
167
168
169
170
171

        if(!isset($config['SHOW_DEBUG_INFO']) || $config['SHOW_DEBUG_INFO'] === 'auto') {
            $config['SHOW_DEBUG_INFO'] = (isset($GLOBALS["TSFE"]->beUserLogin) && $GLOBALS["TSFE"]->beUserLogin === true) ? 'yes' : 'no';
        }

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

175
176
177
    /**
     * @param array $dataArray
     * @param $store
178
179
180
     * @param bool|false $flagOverwrite
     * @throws UserException
     * @throws \qfq\CodeException
181
     */
182
    public function setVarArray(array $dataArray, $store, $flagOverwrite = false) {
Carsten  Rose's avatar
Carsten Rose committed
183
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
184
        if (!isset(self::$sanitizeStore))
Carsten  Rose's avatar
Carsten Rose committed
185
186
187
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);


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

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

195
196
        self::$raw[$store] = $dataArray;
    }
197

198
199
200
201
    /**
     * @param $bodytext
     * @throws CodeException
     */
202
203
    private function fillStoreTypo3($bodytext) {

204
        $arr = KeyValueStringParser::parse($bodytext, "=", "\n");
205
206
207
208
209
210
211
212
213

        if (isset($GLOBALS["TSFE"]->fe_user->user["username"]))
            $arr[TYPO3_FE_USER] = $GLOBALS["TSFE"]->fe_user->user["username"];

        if (isset($GLOBALS["TSFE"]->fe_user->user["uid"]))
            $arr[TYPO3_FE_USER_UID] = $GLOBALS["TSFE"]->fe_user->user["uid"];

        if (isset($GLOBALS["TSFE"]->fe_user->user["usergroup"]))
            $arr[TYPO3_FE_USER_GROUP] = $GLOBALS["TSFE"]->fe_user->user["usergroup"];
214

Carsten  Rose's avatar
Carsten Rose committed
215
216
217
        if (isset($GLOBALS["TSFE"]->page["uid"]))
            $arr[TYPO3_TT_CONTENT_UID] = $GLOBALS["TSFE"]->page["uid"];

218
219
220
        if (isset($GLOBALS["TSFE"]->id))
            $arr[TYPO3_PAGE_ID] = $GLOBALS["TSFE"]->id;

221
        self::setVarArray($arr, STORE_TYPO3, true);
222
    }
223

224
225
226
227
    /**
     * @throws CodeException
     */
    private function fillStoreClient() {
228
229
        // copy GET and POST and SERVER Parameter. Priority: SERVER, POST, GET
        $arr = array_merge($_GET, $_POST, $_SERVER);
230

231
        self::setVarArray($arr, STORE_CLIENT, true);
232
    }
233

Carsten  Rose's avatar
Carsten Rose committed
234
235
    /**
     * @throws CodeException
236
     * @throws UserException
Carsten  Rose's avatar
Carsten Rose committed
237
     */
238
    private function fillStoreSip() {
Carsten  Rose's avatar
Carsten Rose committed
239

240
        $sessionName = self::getVar(SYSTEM_SESSIONNAME, STORE_SYSTEM);
241
        self::$sip = new Sip($sessionName);
242

243
244
245
246
        $s = self::getVar(CLIENT_SIP, STORE_CLIENT);
        if ($s !== false) {
            // if session is given, copy values to store
            $param = self::$sip->getVarsFromSip($s);
247
248
            $param[SIP_SIP] = $s;
            $param[SIP_URLPARAM] = self::$sip->getQueryStringFromSip($s);
249

250
//            self::setVarArray(KeyValueStringParser::parse($param, "=", "&"), STORE_SIP);
251
            self::setVarArray($param, STORE_SIP, true);
252
253
254
        }
    }

255
    /**
256
     * Cycles through all stores in $useStore.
257
     * First match will return the found value.
Carsten  Rose's avatar
Carsten Rose committed
258
     * During cycling: fill cache with requestet value and sanitize raw value.
259
     *
260
     * @param string $key
261
     * @param string $useStores f.e.: 'FSRD'
Carsten  Rose's avatar
Carsten Rose committed
262
     * @param string $sanitizeClass
Carsten  Rose's avatar
Carsten Rose committed
263
     * @param string $foundInStore Returns the name of the store where $key has been found. If $key is not found, return ''.
264
     * @return string a) if found: value, b) false
Carsten  Rose's avatar
Carsten Rose committed
265
     * @throws \qfq\CodeException
266
     */
Carsten  Rose's avatar
Carsten Rose committed
267
    public static function getVar($key, $useStores = STORE_USE_DEFAULT, $sanitizeClass = '', &$foundInStore = '') {
268
269

        // no store specifed?
270
        if ($useStores === "" || $useStores === null) {
271
            $useStores = STORE_USE_DEFAULT;
272
273
        }

Carsten  Rose's avatar
Carsten Rose committed
274
275
276
        // no sanitizeClass specified: take last/default
        if ($sanitizeClass === '') {
            $sanitizeClass = isset(self::$sanitizeClass[$key]) ? self::$sanitizeClass[$key] : SANITIZE_DEFAULT;
277
278
        }

279
280
281
        while ($useStores !== false) {

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

285
            if (!isset(self::$raw[$store][$key])) {
286
                if ($store === STORE_ZERO) {
Carsten  Rose's avatar
Carsten Rose committed
287
288
289
290
                    return 0;
                } else {
                    continue; // no value provided
                }
291
292
            }

293
            $rawVal = isset(self::$raw[$store][$key]) ? self::$raw[$store][$key] : null;
Carsten  Rose's avatar
Carsten Rose committed
294
295
            if (self::$sanitizeStore[$store] && $sanitizeClass != '') {
                return \qfq\Sanitize::sanitize($rawVal, $sanitizeClass);
296
297
            } else {
                return $rawVal;
298
            }
299
        }
Carsten  Rose's avatar
Carsten Rose committed
300
        $foundInStore = '';
301
        return false;
302
    }
303

304
305
    /**
     * @param string $bodytext
306
     * @param bool|false $phpUnit
307
     * @return null|\qfq\Store
308
     */
309
    public static function getInstance($bodytext = '', $phpUnit = false) {
310

311
        if ($phpUnit) {
312
313
314
315
316
317
318
            if (self::$instance !== null) {

                self::unsetStore(STORE_TYPO3);
                self::fillStoreTypo3($bodytext);

                self::unsetStore(STORE_CLIENT);
                self::fillStoreClient();
319
320
321
322
323
324
            }
        }

        // Design Pattern: Singleton
        if (self::$instance === null) {
            self::$instance = new self($bodytext);
Carsten  Rose's avatar
Carsten Rose committed
325
326
327
328
        } 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);
329
330
331
        }

        return self::$instance;
332
    }
333

334
335
336
337
    /**
     * @param $store
     */
    public static function unsetStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
338
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
339
        if (!isset(self::$sanitizeStore))
Carsten  Rose's avatar
Carsten Rose committed
340
341
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);

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

345
346
347
        if (isset(self::$raw[$store])) {
            self::$raw[$store] = array();
        }
Carsten  Rose's avatar
Carsten Rose committed
348

349
350
    }

351
352
353
354
    /**
     * @param $formName
     * @throws CodeException
     */
355
356
    public
    static function createSipAfterFormLoad($formName) {
357
        $recordId = self::getVar(CLIENT_RECORD_ID, STORE_TYPO3 . STORE_CLIENT);
358
359
360
361
362
363
364
        if ($recordId === false) {
            $recordId = 0;
        }

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

        // Construct fake urlparam
365
        $tmpUrlparam = OnArray::toString($tmpParam);
366
367

        // Create a fake SIP which has never been passed by URL - further processing might expect this to exist.
368
        $sip = self::getSipInstance()->queryStringToSip($tmpUrlparam, RETURN_SIP);
369
        self::setVar(CLIENT_SIP, $sip, STORE_CLIENT);
370
371
372
373
374

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

375
376
377
    }

    /**
378
     * @return null|Sip
379
     */
380
    public static function getSipInstance() {
381
382
        return self::$sip;
    }
383
384
385
386
387

    /**
     * @param $key
     * @param $value
     * @param $store
Carsten  Rose's avatar
Carsten Rose committed
388
389
     * @param bool|true $overWrite
     * @throws UserException
390
     */
Carsten  Rose's avatar
Carsten Rose committed
391
392
    public static function setVar($key, $value, $store, $overWrite = true) {
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
393
        if (!isset(self::$sanitizeStore))
Carsten  Rose's avatar
Carsten Rose committed
394
395
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);

396
        if ($store === STORE_ZERO)
Carsten  Rose's avatar
Carsten Rose committed
397
398
399
            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])) {
400
            throw new UserException("Value of '$key' already be set in store '$store'.", ERROR_STORE_KEY_EXIST);
Carsten  Rose's avatar
Carsten Rose committed
401
        }
402
403

        self::$raw[$store][$key] = $value;
404
405
406
407
408
409
410
    }

    /**
     * @param $store
     * @return mixed
     */
    public static function getStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
411
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
412
        if (!isset(self::$sanitizeStore[$store]))
Carsten  Rose's avatar
Carsten Rose committed
413
414
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);

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

418
419
420
421
422
        if (isset(self::$raw[$store])) {
            return self::$raw[$store];
        }
        return array();
    }
423

Carsten  Rose's avatar
Carsten Rose committed
424

425
    /**
Carsten  Rose's avatar
Carsten Rose committed
426
427
     * Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
     *
428
429
430
     * @param $tableName
     * @throws CodeException
     */
431
    public function fillStoreTableDefaultColumnType($tableName) {
432
433
434
435
        $db = new qfq\Database();

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

436
437
438
        self::setVarArray(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT);
        self::setVarArray(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES);
    }
439
440
441
442
443
}