Store.php 13.5 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

            $config['SQLLOG'] = Support::ifRelativePathPrependExtensionPath($config['SQLLOG']);

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

293
        return false;
294
    }
295

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

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

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

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

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

        return self::$instance;
320
    }
321

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

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

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

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

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

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

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

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

362
363
364
    }

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

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

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

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

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

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

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

Carsten  Rose's avatar
Carsten Rose committed
411

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

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

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