forked from felixsemmler/Realurl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
class.tx_realurl.php
2884 lines (2584 loc) · 108 KB
/
class.tx_realurl.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/***************************************************************
* Copyright notice
*
* (c) 2004 Kasper Skaarhoj (kasper@typo3.com)
* (c) 2005-2010 Dmitry Dulepov (dmitry@typo3.org)
* All rights reserved
*
* This script is part of the Typo3 project. The Typo3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
* A copy is found in the textfile GPL.txt and important notices to the license
* from the author is found in LICENSE.txt distributed with these scripts.
*
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
/**
* Class for creating and parsing Speaking Urls
*
* $Id$
*
* @author Kasper Skaarhoj <kasper@typo3.com>
* @author Dmitry Dulepov <dmitry@typo3.org>
*/
/**
* Class for creating and parsing Speaking Urls
* This class interfaces with hooks in TYPO3 inside tslib_fe (for parsing speaking URLs to GET parameters) and in t3lib_tstemplate (for parsing GET parameters into a speaking URL)
*
* @author Kasper Skaarhoj <kasper@typo3.com>
* @author Dmitry Dulepov <dmitry@typo3.org>
* @package TYPO3
* @subpackage tx_realurl
*/
class tx_realurl {
// External, static
var $NA = '-'; // Substitute value for "blank" values
var $maxLookUpLgd = 100; // Max. length of look-up strings. Just a "brake"
var $prefixEnablingSpURL = 'index.php'; // Only work Speaking URL on URLs starting with "index.php"
var $decodeCacheTTL = 1; // TTL for decode cache, default is 1 day.
var $encodeCacheTTL = 1; // TTL for encode cache, default is 1 day.
// Internal
/** @var tslib_fe */
var $pObj; // tslib_fe / GLOBALS['TSFE'] (for ->decodeSpURL())
var $extConf; // Configuration for extension, from $TYPO3_CONF_VARS['EXTCONF']['realurl']
var $adminJumpSet = FALSE; // Is set true (->encodeSpURL) if AdminJump is active in some way. Is set false again when captured first time!
var $fe_user_prefix_set = FALSE; // Is set true (->encodeSpURL) if there is a frontend user logged in
var $filePart; // Contains the filename when a Speaking URL is decoded.
var $dirParts; // All directory parts of the string
var $orig_paramKeyValues = array(); // Contains the index of GETvars that the URL had when the encoding began.
var $appendedSlash = false; // Set true if slash is appended
var $encodePageId = 0; // Set with the page id during encoding. for internal use only.
var $speakingURIpath_procValue = ''; // For decoding, the path we are processing.
var $disableDecodeCache = FALSE; // If set internally, decode caching is disabled. Used when a 303 header is set in tx_realurl_advanced.
var $decode_editInBackend = FALSE; // If set (in adminjump function) then we will redirect to edit the found page id in the backend.
var $encodeError = FALSE; // If set true encoding failed , probably because the url was outside of root line - and the input url is returned directly.
var $host = ''; // Current host name. Set in setConfig()
/**
* Additional values to use when creating chash cache. This works, for
* example, when using _DOMAINS and cHash for links that do not really
* need a cHash.
*
* @var array
*/
protected $additionalParametersForChash;
/**
* Actual host name (configuration key) for the current request. This can
* be different from the $this->host if there are host aliases.
*
* @var string
*/
protected $hostConfigured = '';
var $multidomain = false;
var $urlPrepend = array();
var $useMySQLExtendedSyntax = false;
/**
* Holds a uid of the detected language during decoding to limit search of
* titles only to this language. Valid values are:
* -1 - no language detected
* 0 - default language (only if really detected!)
* >0 - a language uid taken from preVars or _DOMAINS (corresponds to uid in sys_languages table)
*
* @var int
*/
protected $detectedLanguage = -1;
/**
* Inidicates wwether devLog is enabled
*
* @var true
*/
protected $enableDevLog = false;
/**
* Contains a request id. This is to simplify identification of a single
* request when the site is accessed concurently
*
* @var string
*/
protected $devLogId;
/**
* Mime type that can be set according to the file extension (decoding only).
*
* @var string
*/
protected $mimeType = null;
var $enableStrictMode = false;
var $enableChashDebug = false;
/**
* If non-empty, corresponding URL query parameter will be ignored in preVars
* (note: preVars only!). This is necessary for _DOMAINS feature. This value
* is set to empty in adjustConfigurationByHostEncode().
*
* @see tx_realurl::adjustConfigurationByHostEncode()
* @see tx_realurl::encodeSpURL_doEncode()
* @var string
*/
protected $ignoreGETvar;
/**
* Contains URL parameters that were merged into URL. This is necessary
* if cHash has to be recalculated due to bypassed parameters. Used during
* encoding only.
*
* @var array
* @see http://bugs.typo3.org/view.php?id=11219
*/
protected $cHashParameters;
/**
* Indicates wether cHash should be rebuilt for the URL. Used during
* encoding only.
*
* @var boolean
* @see http://bugs.typo3.org/view.php?id=11219
*/
protected $rebuildCHash;
/************************************
*
* Translate parameters to a Speaking URL (t3lib_tstemplate::linkData)
*
************************************/
/**
* Creates an instance of this class
*/
public function __construct() {
if (!t3lib_extMgm::isLoaded('dbal')) {
// allow to use the MySQL features of 5.x with mysqli
$this->useMySQLExtendedSyntax = TRUE;
}
$sysconf = (array)unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['realurl']);
$this->enableStrictMode = (bool)$sysconf['enableStrictMode'];
$this->enableChashUrlDebug = (bool)$sysconf['enableChashUrlDebug'];
$this->initDevLog($sysconf);
}
/**
* Initializes devLog support
*
* @param array $sysconf
* @return void
*/
protected function initDevLog(array $sysconf) {
$this->enableDevLog = (boolean)$sysconf['enableDevLog'];
if ($this->enableDevLog) {
$this->devLogId = (isset($_SERVER['UNIQUE_ID']) ? $_SERVER['UNIQUE_ID'] : uniqid(''));
}
}
/**
* Translates a URL with query string (GET parameters) into Speaking URL.
* Called from t3lib_tstemplate::linkData
*
* @param array Array of parameters from t3lib_tstemplate::linkData - the function creating all links inside TYPO3
* @return void
*/
public function encodeSpURL(&$params) {
$this->devLog('Entering encodeSpURL for ' . $params['LD']['totalURL']);
if ($this->isInWorkspace()) {
$this->devLog('Workspace detected. Not doing anything!');
return;
}
if (!$params['TCEmainHook']) {
// Return directly, if simulateStaticDocuments is set
if ($GLOBALS['TSFE']->config['config']['simulateStaticDocuments']) {
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TT']->setTSlogMessage('SimulateStaticDocuments is enabled. RealURL disables itself.', 2);
return;
}
// Return directly, if realurl is not enabled
if (!$GLOBALS['TSFE']->config['config']['tx_realurl_enable']) {
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TT']->setTSlogMessage('RealURL is not enabled in TS setup. Finished.');
return;
}
}
// Checking prefix
$prefix = $GLOBALS['TSFE']->absRefPrefix . $this->prefixEnablingSpURL;
if (substr($params['LD']['totalURL'], 0, strlen($prefix)) != $prefix) {
return;
}
$this->devLog('Starting URL encode');
// Initializing config / request URL
$this->setConfig();
$adjustedConfiguration = $this->adjustConfigurationByHost('encode', $params);
$this->adjustRootPageId();
$internalExtras = array();
// Init "Admin Jump"; If frontend edit was enabled by the current URL of the page, set it again in the generated URL (and disable caching!)
if (!$params['TCEmainHook']) {
if ($GLOBALS['TSFE']->applicationData['tx_realurl']['adminJumpActive']) {
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TSFE']->set_no_cache();
$this->adminJumpSet = TRUE;
$internalExtras['adminJump'] = 1;
}
// If there is a frontend user logged in, set fe_user_prefix
if (is_array($GLOBALS['TSFE']->fe_user->user)) {
$this->fe_user_prefix_set = TRUE;
$internalExtras['feLogin'] = 1;
}
}
// Parse current URL into main parts
$uParts = parse_url($params['LD']['totalURL']);
// Look in memory cache first
$urlData = $this->hostConfigured . ' | ' . $uParts['query'];
$newUrl = $this->encodeSpURL_encodeCache($urlData, $internalExtras);
if (!$newUrl) {
// Encode URL
$newUrl = $this->encodeSpURL_doEncode($uParts['query'], $params['LD']['totalURL']);
// Set new URL in cache
$this->encodeSpURL_encodeCache($urlData, $internalExtras, $newUrl);
}
unset($urlData);
// Adding any anchor there might be
if ($uParts['fragment']) {
$newUrl .= '#' . $uParts['fragment'];
}
// Reapply config.absRefPrefix if necessary
if ((!isset($this->extConf['init']['reapplyAbsRefPrefix']) || $this->extConf['init']['reapplyAbsRefPrefix']) && $GLOBALS['TSFE']->absRefPrefix) {
// Prevent // in case of absRefPrefix ending with / and emptyUrlReturnValue=/
if (substr($GLOBALS['TSFE']->absRefPrefix, -1, 1) == '/' && substr($newUrl, 0, 1) == '/') {
$newUrl = substr($newUrl, 1);
}
$newUrl = $GLOBALS['TSFE']->absRefPrefix . $newUrl;
}
// Set prepending of URL (e.g. hostname) which will be processed by typoLink_PostProc hook in tslib_content
if (isset($adjustedConfiguration['urlPrepend']) && !isset($this->urlPrepend[$newUrl])) {
$urlPrepend = $adjustedConfiguration['urlPrepend'];
if (substr($urlPrepend, -1) == '/') {
$urlPrepend = substr($urlPrepend, 0, -1);
}
$this->urlPrepend[$newUrl] = $urlPrepend;
}
// Call hooks
if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['encodeSpURL_postProc'])) {
foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['encodeSpURL_postProc'] as $userFunc) {
$hookParams = array(
'pObj' => &$this,
'params' => $params,
'URL' => &$newUrl,
);
t3lib_div::callUserFunction($userFunc, $hookParams, $this);
}
}
// Setting the encoded URL in the LD key of the params array - that value is passed by reference and thus returned to the linkData function!
$params['LD']['totalURL'] = $newUrl;
}
/**
* Prepends URL generated by RealURL by something (e.g. a host).
* This method gets called by the typoLink_PostProc hook in tslib_content:
*
* @param array $parameters Array of parameters from typoLink_PostProc hook in tslib_content
* @param tslib_cObj $pObj Reference to the calling tslib_content instance
* @return void
*/
public function encodeSpURL_urlPrepend(&$parameters, &$pObj) {
if (isset($parameters['finalTagParts']['url'])) {
// We must check for absolute URLs here because typolink can force
// absolute URLs for pages with restricted access. It prepends
// current host always. See http://bugs.typo3.org/view.php?id=18200
$testUrl = $parameters['finalTagParts']['url'];
if (preg_match('/^https?:\/\/[^\/]+\//', $testUrl)) {
$testUrl = preg_replace('/https?:\/\/[^\/]+(.+)$/', '\1', $testUrl);
}
if (isset($this->urlPrepend[$testUrl])) {
$urlKey = $url = $testUrl;
// Remove absRefPrefix if necessary
$absRefPrefixLength = strlen($GLOBALS['TSFE']->absRefPrefix);
if ($absRefPrefixLength != 0 && substr($url, 0, $absRefPrefixLength) == $GLOBALS['TSFE']->absRefPrefix) {
$url = substr($url, $absRefPrefixLength);
}
$url = $this->urlPrepend[$urlKey] . ($url{0} != '/' ? '/' : '') . $url;
unset($this->urlPrepend[$testUrl]);
// Adjust the URL
$parameters['finalTag'] = str_replace(
'"' . htmlspecialchars($parameters['finalTagParts']['url']) . '"',
'"' . htmlspecialchars($url) . '"',
$parameters['finalTag']
);
$parameters['finalTagParts']['url'] = $url;
$pObj->lastTypoLinkUrl = $url;
}
}
}
/**
* Transforms a query string into a speaking URL according to the configuration in ->extConf
*
* @param string $inputQuery Input query string
* @param string $origUrl Original URL
* @return string Output Speaking URL (with as many GET parameters encoded into the URL as possible).
* @see encodeSpURL()
*/
protected function encodeSpURL_doEncode($inputQuery, $origUrl = '') {
$this->cHashParameters = array();
$this->rebuildCHash = false;
// Extract all GET parameters into an ARRAY
$paramKeyValues = array();
$GETparams = explode('&', $inputQuery);
foreach ($GETparams as $paramAndValue) {
list($p, $v) = explode('=', $paramAndValue, 2);
$p = urldecode($p);
if ($p != '') {
$paramKeyValues[$p] = urldecode($v);
}
}
$this->orig_paramKeyValues = $paramKeyValues;
// Init array in which to collect the "directories" of the URL
$pathParts = array();
// Pre-vars
$this->encodeSpURL_setSequence($this->extConf['preVars'], $paramKeyValues, $pathParts);
// Create path from ID value
$page_id = $this->encodePageId = $paramKeyValues['id'];
$this->encodeError = FALSE;
$this->encodeSpURL_pathFromId($paramKeyValues, $pathParts);
if ($this->encodeError) {
return $origUrl;
}
// Fixed Post-vars
$fixedPostVarSetCfg = $this->getPostVarSetConfig($page_id, 'fixedPostVars');
if (is_array($fixedPostVarSetCfg)) {
$this->encodeSpURL_setSequence($fixedPostVarSetCfg, $paramKeyValues, $pathParts);
}
// Post var sets
$postVarSetCfg = $this->getPostVarSetConfig($page_id);
$this->encodeSpURL_gettingPostVarSets($paramKeyValues, $pathParts, $postVarSetCfg);
// Compile Speaking URL path
$pathParts = $this->cleanUpPathParts($pathParts);
// Add filename, if any
$newUrl = $this->createURLWithFileName($paramKeyValues, $pathParts);
// Fix empty URLs
$newUrl = $this->fixEmptyUrl($newUrl);
// Clear ignored var
if (isset($paramKeyValues[$this->ignoreGETvar])) {
unset($paramKeyValues[$this->ignoreGETvar]);
}
// Process cHash
$this->encodeSpURL_cHashProcessing($newUrl, $paramKeyValues);
// Manage remaining GET parameters
if (count($paramKeyValues)) {
$q = array();
foreach ($paramKeyValues as $k => $v) {
$q[] = $this->rawurlencodeParam($k) . '=' . rawurlencode($v);
}
$newUrl .= '?' . implode('&', $q);
}
// Memory clean up
unset($this->cHashParameters);
// Return new, Speaking URL encoded URL
return $newUrl;
}
/**
* Creating the TYPO3 Page path into $pathParts from the "id" value in $paramKeyValues
*
* @param array $paramKeyValues Current URLs GETvar => value pairs in array, being translated into pathParts: Here we take out "id" GET var.
* @param array $pathParts Numerical array of path-parts, continously being filled. Here, the "page path" is being added by which-ever method is preferred. Passed by reference.
* @return void Unsetting "id" from $paramKeyValues / Setting page path in $pathParts
* @see encodeSpURL_doEncode()
*/
protected function encodeSpURL_pathFromId(&$paramKeyValues, &$pathParts) {
// Return immediately if no GET vars remain to be translated
if (!count($paramKeyValues)) {
return;
}
// Creating page path
switch ((string)$this->extConf['pagePath']['type']) {
case 'user':
$params = array('paramKeyValues' => &$paramKeyValues, 'pathParts' => &$pathParts, 'pObj' => &$this, 'conf' => $this->extConf['pagePath'], 'mode' => 'encode');
t3lib_div::callUserFunction($this->extConf['pagePath']['userFunc'], $params, $this);
break;
default: // Default: Just passing through the ID/alias of the page:
$pathParts[] = rawurlencode($paramKeyValues['id']);
unset($paramKeyValues['id']);
break;
}
}
/**
* Traversing setup for variables AFTER the page path.
*
* @param array $paramKeyValues Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
* @param array $pathParts Numerical array of path-parts, continously being filled. Passed by reference.
* @param array $postVarSetCfg config
* @return void Removing values from $paramKeyValues / Setting values in $pathParts
* @see encodeSpURL_doEncode(), decodeSpURL_settingPostVarSets()
*/
protected function encodeSpURL_gettingPostVarSets(&$paramKeyValues, &$pathParts, $postVarSetCfg) {
// Traverse setup for postVarSets. If any of those matches
if (is_array($postVarSetCfg)) {
foreach ($postVarSetCfg as $keyWord => $cfg) {
switch ((string)$cfg['type']) {
case 'admin':
if ($this->adminJumpSet) {
$pathParts[] = rawurlencode($keyWord);
$this->adminJumpSet = FALSE; // ... this makes sure that any subsequent "admin-jump" activation is set...
}
break;
case 'single':
$this->encodeSpURL_setSingle($keyWord, $cfg['keyValues'], $paramKeyValues, $pathParts);
break;
default:
unset($cfg['type']); // Just to make sure it is NOT set.
foreach ($cfg as $Gcfg) {
if (isset($paramKeyValues[$Gcfg['GETvar']])) {
$pathParts[] = rawurlencode($keyWord);
$pathPartsSize = count($pathParts);
$cHashParameters = $this->cHashParameters;
$this->encodeSpURL_setSequence($cfg, $paramKeyValues, $pathParts);
// If (1) nothing was added or (2) only empty segments added, remove this part completely
if (count($pathParts) == $pathPartsSize) {
array_pop($pathParts);
}
else {
$dropSegment = true;
for ($i = $pathPartsSize; $i < count($pathParts); $i++) {
if ($pathParts[$i] != '') {
$dropSegment = false;
break;
}
}
if ($dropSegment) {
$pathParts = array_slice($pathParts, 0, $pathPartsSize - 1);
// Nothing goes to cHash from this part.
$this->cHashParameters = $cHashParameters;
}
}
break;
}
}
break;
}
}
}
}
/**
* Setting a filename if any filename is configured to match remaining variables.
*
* @param array $paramKeyValues Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
* @return string Returns the filename to prepend, if any
* @see encodeSpURL_doEncode(), decodeSpURL_fileName()
*/
protected function encodeSpURL_fileName(array &$paramKeyValues) {
// Look if any filename matches the remaining variables
if (is_array($this->extConf['fileName']['index'])) {
foreach ($this->extConf['fileName']['index'] as $keyWord => $cfg) {
$pathParts = array();
if ($this->encodeSpURL_setSingle($keyWord, $cfg['keyValues'], $paramKeyValues, $pathParts)) {
return $keyWord != '_DEFAULT' ? $keyWord : '';
}
}
}
return '';
}
/**
* Traverses a set of GETvars configured (array of segments)
*
* @param array $varSetCfg Array of segment-configurations.
* @param array $paramKeyValues Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
* @param array $pathParts Numerical array of path-parts, continously being filled. Passed by reference.
* @return void Removing values from $paramKeyValues / Setting values in $pathParts
* @see encodeSpURL_doEncode(), encodeSpURL_gettingPostVarSets(), decodeSpURL_getSequence()
*/
protected function encodeSpURL_setSequence($varSetCfg, &$paramKeyValues, &$pathParts) {
// Traverse array of segments configuration
$prevVal = '';
if (is_array($varSetCfg)) {
foreach ($varSetCfg as $setup) {
switch ($setup['type']) {
case 'action':
$pathPartVal = '';
// Look for admin jump
if ($this->adminJumpSet) {
foreach ($setup['index'] as $pKey => $pCfg) {
if ((string)$pCfg['type'] == 'admin') {
$pathPartVal = $pKey;
$this->adminJumpSet = FALSE;
break;
}
}
}
// Look for frontend user login
if ($this->fe_user_prefix_set) {
foreach ($setup['index'] as $pKey => $pCfg) {
if ((string)$pCfg['type'] == 'feLogin') {
$pathPartVal = $pKey;
$this->fe_user_prefix_set = FALSE;
break;
}
}
}
// If either pathPartVal has been set OR if _DEFAULT type is not bypass, set a value
if (strlen($pathPartVal) || $setup['index']['_DEFAULT']['type'] != 'bypass') {
// If admin jump did not set $pathPartVal, look for first pass-through (no "type" set)
if (!strlen($pathPartVal)) {
foreach ($setup['index'] as $pKey => $pCfg) {
if (!strlen($pCfg['type'])) {
$pathPartVal = $pKey;
break;
}
}
}
// Setting part of path
$pathParts[] = rawurlencode(strlen($pathPartVal) ? $pathPartVal : $this->NA);
}
break;
default:
if (!is_array($setup['cond']) || $this->checkCondition($setup['cond'], $prevVal)) {
// Looking if the GET var is found in parameter index
$GETvar = $setup['GETvar'];
if ($GETvar == $this->ignoreGETvar) {
// Do not do anything with this var!
continue;
}
$parameterSet = isset($paramKeyValues[$GETvar]);
$GETvarVal = $parameterSet ? $paramKeyValues[$GETvar] : '';
// Set reverse map
$revMap = is_array($setup['valueMap']) ? array_flip($setup['valueMap']) : array();
if (isset($revMap[$GETvarVal])) {
$prevVal = $GETvarVal;
$pathParts[] = rawurlencode($revMap[$GETvarVal]);
$this->cHashParameters[$GETvar] = $GETvarVal;
} elseif ($setup['noMatch'] == 'bypass') {
// If no match in reverse value map and "bypass" is set, remove the parameter from the URL
// Must rebuild cHash because we remove a parameter!
$this->rebuildCHash |= $parameterSet;
} elseif ($setup['noMatch'] == 'null') {
// If no match and "null" is set, then set "dummy" value
// Set "dummy" value (?)
$prevVal = '';
$pathParts[] = '';
$this->rebuildCHash |= $parameterSet;
} elseif ($setup['userFunc']) {
$params = array(
'pObj' => &$this,
'value' => $GETvarVal,
'decodeAlias' => false,
'pathParts' => &$pathParts,
'setup' => $setup
);
$prevVal = $GETvarVal;
$GETvarVal = t3lib_div::callUserFunction($setup['userFunc'], $params, $this);
$pathParts[] = rawurlencode($GETvarVal);
$this->cHashParameters[$GETvar] = $prevVal;
} elseif (is_array($setup['lookUpTable'])) {
$prevVal = $GETvarVal;
$GETvarVal = $this->lookUpTranslation($setup['lookUpTable'], $GETvarVal);
$pathParts[] = rawurlencode($GETvarVal);
$this->cHashParameters[$GETvar] = $prevVal;
} elseif (isset($setup['valueDefault'])) {
$prevVal = $setup['valueDefault'];
$pathParts[] = rawurlencode($setup['valueDefault']);
$this->cHashParameters[$GETvar] = $setup['valueMap'][$setup['valueDefault']];
$this->rebuildCHash |= !$parameterSet;
} else {
$prevVal = $GETvarVal;
$pathParts[] = rawurlencode($GETvarVal);
$this->cHashParameters[$GETvar] = $prevVal;
$this->rebuildCHash |= !$parameterSet;
}
// Finally, unset GET var so it doesn't get processed once more
unset($paramKeyValues[$setup['GETvar']]);
}
break;
}
}
}
}
/**
* Traversing an array of GETvar => value pairs and checking if both variable names AND values are matching any found in $paramKeyValues; If so, the keyword representing those values is set and the GEtvars are unset from $paramkeyValues array
*
* @param string $keyWord Keyword to set as a representation of the GETvars configured.
* @param array $keyValues Array of GETvar => values which content in $paramKeyvalues must match exactly in order to be substituted with the keyword, $keyWord
* @param array $paramKeyValues Current URLs GETvar => value pairs in array, being translated into pathParts, continously shortend. Passed by reference.
* @param array $pathParts Numerical array of path-parts, continously being filled. Passed by reference.
* @return boolean Return true, if any value from $paramKeyValues was removed.
* @see encodeSpURL_fileName(), encodeSpURL_gettingPostVarSets(), decodeSpURL_getSingle()
*/
protected function encodeSpURL_setSingle($keyWord, $keyValues, &$paramKeyValues, &$pathParts) {
if (is_array($keyValues)) {
$allSet = TRUE;
// Check if all GETvars configured are found in $paramKeyValues
foreach ($keyValues as $getVar => $value) {
if (!isset($paramKeyValues[$getVar]) || strcmp($paramKeyValues[$getVar], $value)) {
$allSet = FALSE;
break;
}
}
// If all is set, unset the GETvars and set the value.
if ($allSet) {
$pathParts[] = rawurlencode($keyWord);
foreach ($keyValues as $getVar => $value) {
$this->cHashParameters[$getVar] = $value;
unset($paramKeyValues[$getVar]);
}
return TRUE;
}
}
return FALSE;
}
/**
* Setting / Getting encoded URL to/from cache (memory cache, but could be extended to database cache)
*
* @param string $urlData Host + the original URL with GET parameters - identifying the cached version to find
* @param array $internalExtras Array with extra data to include in encoding. This is flags if adminJump url or feLogin flags are set since these are NOT a part of the URL to encode and therefore are needed for the hash to be true.
* @param string $setEncodedURL If set, this URL will be cached as the encoded version of $urlToEncode. Otherwise the function will look for and return the cached version of $urlToEncode
* @return mixed If $setEncodedURL is true, this will be STORED as the cached version and the function returns false, otherwise the cached version is returned (string).
* @see encodeSpURL()
*/
protected function encodeSpURL_encodeCache($urlData, $internalExtras, $setEncodedURL = '') {
// Create hash string
$hash = md5($urlData . '///' . serialize($internalExtras));
if (!$setEncodedURL) { // Asking for cached encoded URL:
// First, check memory, otherwise ask database
if (!isset($GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash]) && $this->extConf['init']['enableUrlEncodeCache']) {
/** @noinspection PhpUndefinedMethodInspection */
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('content', 'tx_realurl_urlencodecache',
'url_hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'tx_realurl_urlencodecache') .
' AND tstamp>' . strtotime('midnight', time() - 24 * 3600 * $this->encodeCacheTTL));
/** @noinspection PhpUndefinedMethodInspection */
if (false != ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
$GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash] = $row['content'];
}
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->sql_free_result($res);
}
return $GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash];
}
else { // Setting encoded URL in cache:
// No caching if FE editing is enabled!
if (!$this->isBEUserLoggedIn()) {
$GLOBALS['TSFE']->applicationData['tx_realurl']['_CACHE'][$hash] = $setEncodedURL;
// If the page id is NOT an integer, it's an alias we have to look up
if (!self::testInt($this->encodePageId)) {
$this->encodePageId = $this->pageAliasToID($this->encodePageId);
}
if ($this->extConf['init']['enableUrlEncodeCache'] && $this->canCachePageURL($this->encodePageId)) {
$insertFields = array(
'url_hash' => $hash,
'origparams' => $urlData,
'internalExtras' => count($internalExtras) ? serialize($internalExtras) : '',
'content' => $setEncodedURL,
'page_id' => $this->encodePageId,
'tstamp' => time()
);
if ($this->useMySQLExtendedSyntax) {
/** @noinspection PhpUndefinedMethodInspection */
$query = $GLOBALS['TYPO3_DB']->INSERTquery('tx_realurl_urlencodecache', $insertFields);
$query .= ' ON DUPLICATE KEY UPDATE tstamp=' . $insertFields['tstamp'];
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->sql_query($query);
} else {
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->sql_query('START TRANSACTION');
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->exec_DELETEquery('tx_realurl_urlencodecache', 'url_hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'tx_realurl_urlencodecache'));
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_urlencodecache', $insertFields);
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->sql_query('COMMIT');
}
}
}
}
return '';
}
/**
* Will store a record in a cachetable holding the value of the "cHash" parameter in a link, if any.
* Background:
* The "cHash" parameter is a hash over the values in the Query String of a URL and it "authenticates" the URL to the frontend so we can safely cache page content with that parameter combination.
* Technically, there is no problem with the "cHash" parameter - it is like any other parameter something we could encode with Speaking URLs. The problem is: a cHash string is not "speaking" (and never will be!)
* So; the only option we are left with if we want to remove the "?cHash=...:" remains in URLs and at the same time do not want to include it in the virtual path is; store it in the database!
* This is what this function does: Stores a record in the database which relates the cHash value to a hash id of the URL. This is done ONLY if the "cHash" parameter is the only one left which would make the URL non-speaking. Otherwise it is left behind.
* Obviously, this whole thing only works if there is a function in the decode part which will look up the cHash again and include it in the GET parameters resolved from the Speaking URL - but there is of course...
*
* @param string $newUrl URL path (being hashed to an integer and cHash value related to this.)
* @param array $paramKeyValues Params $array array, passed by reference. If "cHash" is the only value left it will be put in the cache table and the value is unset in the array.
* @return void
* @see decodeSpURL_cHashCache()
*/
protected function encodeSpURL_cHashProcessing($newUrl, &$paramKeyValues) {
// If "cHash" is the ONLY parameter left...
// (if there are others our problem is that the cHash probably covers those
// as well and if we include the cHash anyways we might get duplicates for
// the same speaking URL in the cache table!)
if (isset($paramKeyValues['cHash'])) {
if ($this->rebuildCHash) {
$cacheHashClassExists = class_exists('t3lib_cacheHash');
$cacheHash = ($cacheHashClassExists ? t3lib_div::makeInstance('t3lib_cacheHash') : NULL);
/** @noinspection PhpUndefinedClassInspection */
/* @var t3lib_cacheHash $cacheHash */
$cHashParameters = array_merge($this->cHashParameters, $paramKeyValues);
unset($cHashParameters['cHash']);
$cHashParameters = t3lib_div::implodeArrayForUrl('', $cHashParameters);
if ($cacheHashClassExists) {
/** @noinspection PhpUndefinedMethodInspection */
$cHashParameters = $cacheHash->getRelevantParameters($cHashParameters);
} else {
/** @noinspection PhpUndefinedMethodInspection PhpDeprecationInspection */
$cHashParameters = t3lib_div::cHashParams($cHashParameters);
}
unset($cHashParameters['']);
if (count($cHashParameters) == 1) {
// No cHash needed.
unset($paramKeyValues['cHash']);
}
elseif (count($cHashParameters) > 1) {
if ($cacheHashClassExists) {
/** @noinspection PhpUndefinedMethodInspection */
$paramKeyValues['cHash'] = $cacheHash->calculateCacheHash($cHashParameters);
} elseif (method_exists('t3lib_div', 'calculateCHash')) {
/** @noinspection PhpUndefinedMethodInspection PhpDeprecationInspection */
$paramKeyValues['cHash'] = t3lib_div::calculateCHash($cHashParameters);
} else {
/** @noinspection PhpUndefinedMethodInspection PhpDeprecationInspection */
$paramKeyValues['cHash'] = t3lib_div::shortMD5(serialize($cHashParameters));
}
}
unset($cHashParameters);
}
if ($this->extConf['init']['enableCHashCache'] && count($paramKeyValues) == 1) {
$stringForHash = $newUrl;
if (count($this->additionalParametersForChash)) {
$stringForHash .= '|' . serialize($this->additionalParametersForChash);
}
$spUrlHash = md5($stringForHash);
/** @noinspection PhpUndefinedMethodInspection */
$spUrlHashQuoted = $GLOBALS['TYPO3_DB']->fullQuoteStr($spUrlHash, 'tx_realurl_chashcache');
// first, look if a cHash is already there for this SpURL
/** @noinspection PhpUndefinedMethodInspection */
list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('chash_string',
'tx_realurl_chashcache', 'spurl_hash=' . $spUrlHashQuoted);
if (!is_array($row)) {
// Nothing found, insert to the cache
$data = array(
'spurl_hash' => $spUrlHash,
'spurl_string' => $this->enableChashUrlDebug ? $stringForHash : null,
'chash_string' => $paramKeyValues['cHash']
);
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->exec_INSERTquery('tx_realurl_chashcache', $data);
}
else {
// If one found, check if it is different, and if so update
if ($row['chash_string'] != $paramKeyValues['cHash']) {
// If that chash_string is different from the one we want to
// insert, that might be a bug or mean that encryptionKey was
// changed so cHash values will be different now
// In any case we will just silently update the value
$data = array(
'chash_string' => $paramKeyValues['cHash']
);
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_realurl_chashcache',
'spurl_hash=' . $spUrlHashQuoted, $data);
}
}
// Unset "cHash" (and array should now be empty!)
unset($paramKeyValues['cHash']);
}
}
}
/************************************
*
* Translate a Speaking URL to parameters (tslib_fe)
*
************************************/
/**
* Parse speaking URL and translate it to parameters understood by TYPO3
* Function is called from tslib_fe
* The overall format of a speaking URL is these five parts [TYPO3_SITE_URL] / [pre-var] / [page-identification] / [post-vars] / [file.ext]
* - "TYPO3_SITE_URL" is fixed value from the environment,
* - "pre-var" is any number of segments separated by "/" mapping to GETvars AND with a known lenght,
* - "page-identification" identifies the page id in TYPO3 possibly with multiple segments separated by "/" BUT with an UNKNOWN length,
* - "post-vars" is sets of segments offering the same features as "pre-var"
* - "file.ext" is any filename that might apply
*
* @param array $params Params for hook
* @return void Setting internal variables.
*/
public function decodeSpURL($params) {
$this->devLog('Entering decodeSpURL');
// Setting parent object reference (which is $GLOBALS['TSFE'])
$this->pObj = &$params['pObj'];
// Initializing config / request URL
$this->setConfig();
$this->adjustConfigurationByHost('decode');
$this->adjustRootPageId();
// If there has been a redirect (basically; we arrived here otherwise than via "index.php" in the URL) this can happend either due to a CGI-script or because of reWrite rule. Earlier we used $GLOBALS['HTTP_SERVER_VARS']['REDIRECT_URL'] to check but...
if ($this->pObj->siteScript && substr($this->pObj->siteScript, 0, 9) != 'index.php' && substr($this->pObj->siteScript, 0, 1) != '?') {
// Getting the path which is above the current site url
// For instance "first/second/third/index.html?¶m1=value1¶m2=value2"
// should be the result of the URL
// "http://localhost/typo3/dev/dummy_1/first/second/third/index.html?¶m1=value1¶m2=value2"
// Note: sometimes in fcgi installations it is absolute, so we have to make it
// relative to work properly.
$speakingURIpath = $this->pObj->siteScript{0} == '/' ? substr($this->pObj->siteScript, 1) : $this->pObj->siteScript;
// Call hooks
if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['decodeSpURL_preProc'])) {
foreach($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['realurl']['decodeSpURL_preProc'] as $userFunc) {
$hookParams = array(
'pObj' => &$this,
'params' => $params,
'URL' => &$speakingURIpath,
);
t3lib_div::callUserFunction($userFunc, $hookParams, $this);
}
}
// Append missing slash if configured for
if ($this->extConf['init']['appendMissingSlash']) {
$regexp = '~^([^\?]*[^/])(\?.*)?$~';
if (substr($speakingURIpath, -1, 1) == '?') {
$speakingURIpath = substr($speakingURIpath, 0, -1);
}
if (preg_match($regexp, $speakingURIpath)) { // Only process if a slash is missing:
$options = t3lib_div::trimExplode(',', $this->extConf['init']['appendMissingSlash'], true);
if (in_array('ifNotFile', $options)) {
if (!preg_match('/\/[^\/\?]+\.[^\/]+(\?.*)?$/', '/' . $speakingURIpath)) {
$speakingURIpath = preg_replace($regexp, '\1/\2', $speakingURIpath);
$this->appendedSlash = true;
}
}
else {
$speakingURIpath = preg_replace($regexp, '\1/\2', $speakingURIpath);
$this->appendedSlash = true;
}
if ($this->appendedSlash && count($options) > 0) {
foreach ($options as $option) {
$matches = array();
if (preg_match('/^redirect(\[(30[1237])\])?$/', $option, $matches)) {
$code = count($matches) > 1 ? $matches[2] : 301;
$status = 'HTTP/1.1 ' . $code . ' TYPO3 RealURL redirect M' . __LINE__;
// Check path segment to be relative for the current site.
// parse_url() does not work with relative URLs, so we use it to test
if (!@parse_url($speakingURIpath, PHP_URL_HOST)) {
@ob_end_clean();
header($status);
header('Location: ' . t3lib_div::locationHeaderUrl($speakingURIpath));
exit;
}
}
}
}
}
}
// If the URL is a single script like "123.1.html" it might be an "old" simulateStaticDocument request. If this is the case and support for this is configured, do NOT try and resolve it as a Speaking URL
$fI = t3lib_div::split_fileref($speakingURIpath);
if (!self::testInt($this->pObj->id) && $fI['path'] == '' && $this->extConf['fileName']['defaultToHTMLsuffixOnPrev'] && $this->extConf['init']['respectSimulateStaticURLs']) {
// If page ID does not exist yet and page is on the root level and both
// respectSimulateStaticURLs and defaultToHTMLsuffixOnPrev are set, than
// ignore respectSimulateStaticURLs and attempt to resolve page id.
// See http://bugs.typo3.org/view.php?id=1530
/** @noinspection PhpUndefinedMethodInspection */
$GLOBALS['TT']->setTSlogMessage('decodeSpURL: ignoring respectSimulateStaticURLs due defaultToHTMLsuffixOnPrev for the root level page!)', 2);
$this->extConf['init']['respectSimulateStaticURLs'] = false;
}
if (!$this->extConf['init']['respectSimulateStaticURLs'] || $fI['path']) {
$this->devLog('RealURL powered decoding (TM) starting!');
// Parse path
$uParts = @parse_url($speakingURIpath);
if (!is_array($uParts)) {
$this->decodeSpURL_throw404('Current URL is invalid');
}
$speakingURIpath = $this->speakingURIpath_procValue = $uParts['path'];