Store.php 13.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/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
178
179
    /**
     * @param array $dataArray
     * @param $store
     * @throws CodeException
     */
180
    public function setVarArray(array $dataArray, $store, $flagOverwrite = false) {
Carsten  Rose's avatar
Carsten Rose committed
181
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
182
        if (!isset(self::$sanitizeStore))
Carsten  Rose's avatar
Carsten Rose committed
183
184
185
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);


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

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

193
194
        self::$raw[$store] = $dataArray;
    }
195

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

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

        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"];
212

213
214
215
        if (isset($GLOBALS["TSFE"]->id))
            $arr[TYPO3_PAGE_ID] = $GLOBALS["TSFE"]->id;

216
        self::setVarArray($arr, STORE_TYPO3, true);
217
    }
218

219
220
221
222
    /**
     * @throws CodeException
     */
    private function fillStoreClient() {
223
224
        // copy GET and POST and SERVER Parameter. Priority: SERVER, POST, GET
        $arr = array_merge($_GET, $_POST, $_SERVER);
225

226
        self::setVarArray($arr, STORE_CLIENT, true);
227
    }
228

Carsten  Rose's avatar
Carsten Rose committed
229
230
    /**
     * @throws CodeException
231
     * @throws UserException
Carsten  Rose's avatar
Carsten Rose committed
232
     */
233
    private function fillStoreSip() {
Carsten  Rose's avatar
Carsten Rose committed
234

235
        $sessionName = self::getVar(SYSTEM_SESSIONNAME, STORE_SYSTEM);
236
        self::$sip = new Sip($sessionName);
237

238
239
240
241
        $s = self::getVar(CLIENT_SIP, STORE_CLIENT);
        if ($s !== false) {
            // if session is given, copy values to store
            $param = self::$sip->getVarsFromSip($s);
242
243
            $param[SIP_SIP] = $s;
            $param[SIP_URLPARAM] = self::$sip->getQueryStringFromSip($s);
244

245
//            self::setVarArray(KeyValueStringParser::parse($param, "=", "&"), STORE_SIP);
246
            self::setVarArray($param, STORE_SIP, true);
247
248
249
        }
    }

250
    /**
251
     * Cycles through all stores in $useStore.
252
     * First match will return the found value.
Carsten  Rose's avatar
Carsten Rose committed
253
     * During cycling: fill cache with requestet value and sanitize raw value.
254
     *
255
     * @param string $key
256
     * @param string $useStores f.e.: 'FSRD'
Carsten  Rose's avatar
Carsten Rose committed
257
     * @param string $sanitizeClass
258
259
     * @return string a) if found: value, b) false
     */
Carsten  Rose's avatar
Carsten Rose committed
260
    public static function getVar($key, $useStores = STORE_USE_DEFAULT, $sanitizeClass = '') {
261
262

        // no store specifed?
263
        if ($useStores === "" || $useStores === null) {
264
            $useStores = STORE_USE_DEFAULT;
265
266
        }

Carsten  Rose's avatar
Carsten Rose committed
267
268
269
        // no sanitizeClass specified: take last/default
        if ($sanitizeClass === '') {
            $sanitizeClass = isset(self::$sanitizeClass[$key]) ? self::$sanitizeClass[$key] : SANITIZE_DEFAULT;
270
271
        }

272
273
274
275
276
        while ($useStores !== false) {

            $store = substr($useStores, 0, 1); // next store
            $useStores = substr($useStores, 1); // shift left remaining stores

277
            if (!isset(self::$raw[$store][$key])) {
278
                if ($store === STORE_ZERO) {
Carsten  Rose's avatar
Carsten Rose committed
279
280
281
282
                    return 0;
                } else {
                    continue; // no value provided
                }
283
284
            }

285
            $rawVal = isset(self::$raw[$store][$key]) ? self::$raw[$store][$key] : null;
286

Carsten  Rose's avatar
Carsten Rose committed
287
288
            if (self::$sanitizeStore[$store] && $sanitizeClass != '') {
                return \qfq\Sanitize::sanitize($rawVal, $sanitizeClass);
289
290
            } else {
                return $rawVal;
291
            }
292
        }
293

294
        return false;
295
    }
296

297
298
    /**
     * @param string $bodytext
299
     * @param bool|false $phpUnit
300
     * @return null|\qfq\Store
301
     */
302
    public static function getInstance($bodytext = '', $phpUnit = false) {
303

304
        if ($phpUnit) {
305
306
307
308
309
310
311
            if (self::$instance !== null) {

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

                self::unsetStore(STORE_CLIENT);
                self::fillStoreClient();
312
313
314
315
316
317
318
319
320
            }
        }

        // Design Pattern: Singleton
        if (self::$instance === null) {
            self::$instance = new self($bodytext);
        }

        return self::$instance;
321
    }
322

323
324
325
326
    /**
     * @param $store
     */
    public static function unsetStore($store) {
Carsten  Rose's avatar
Carsten Rose committed
327
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
328
        if (!isset(self::$sanitizeStore))
Carsten  Rose's avatar
Carsten Rose committed
329
330
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);

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

334
335
336
337
338
        if (isset(self::$raw[$store])) {
            self::$raw[$store] = array();
        }
    }

339
340
341
342
    /**
     * @param $formName
     * @throws CodeException
     */
343
344
    public
    static function createSipAfterFormLoad($formName) {
345
        $recordId = self::getVar(CLIENT_RECORD_ID, STORE_TYPO3 . STORE_CLIENT);
346
347
348
349
350
351
352
        if ($recordId === false) {
            $recordId = 0;
        }

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

        // Construct fake urlparam
353
        $tmpUrlparam = OnArray::toString($tmpParam);
354
355

        // Create a fake SIP which has never been passed by URL - further processing might expect this to exist.
356
        $sip = self::getSipInstance()->queryStringToSip($tmpUrlparam, RETURN_SIP);
357
        self::setVar(CLIENT_SIP, $sip, STORE_CLIENT);
358
359
360
361
362

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

363
364
365
    }

    /**
366
     * @return null|Sip
367
     */
368
    public static function getSipInstance() {
369
370
        return self::$sip;
    }
371
372
373
374
375

    /**
     * @param $key
     * @param $value
     * @param $store
Carsten  Rose's avatar
Carsten Rose committed
376
377
     * @param bool|true $overWrite
     * @throws UserException
378
     */
Carsten  Rose's avatar
Carsten Rose committed
379
380
    public static function setVar($key, $value, $store, $overWrite = true) {
        // Check valid Storename
Carsten  Rose's avatar
Carsten Rose committed
381
        if (!isset(self::$sanitizeStore))
Carsten  Rose's avatar
Carsten Rose committed
382
383
            throw new UserException("Unknown Store: $store", ERROR_UNNOWN_STORE);

384
        if ($store === STORE_ZERO)
Carsten  Rose's avatar
Carsten Rose committed
385
386
387
            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])) {
388
            throw new UserException("Value of '$key' already be set in store '$store'.", ERROR_STORE_KEY_EXIST);
Carsten  Rose's avatar
Carsten Rose committed
389
        }
390
391

        self::$raw[$store][$key] = $value;
392
393
394
395
396
397
398
    }

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

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

406
407
408
409
410
        if (isset(self::$raw[$store])) {
            return self::$raw[$store];
        }
        return array();
    }
411

Carsten  Rose's avatar
Carsten Rose committed
412

413
    /**
Carsten  Rose's avatar
Carsten Rose committed
414
415
     * Fills STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES
     *
416
417
418
     * @param $tableName
     * @throws CodeException
     */
419
    public function fillStoreTableDefaultColumnType($tableName) {
420
421
422
423
        $db = new qfq\Database();

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

424
425
426
        self::setVarArray(array_column($tableDefinition, 'Default', 'Field'), STORE_TABLE_DEFAULT);
        self::setVarArray(array_column($tableDefinition, 'Type', 'Field'), STORE_TABLE_COLUMN_TYPES);
    }
427
428
429
430
431
}