mirror of
				https://github.com/slawkens/myaac.git
				synced 2025-10-26 14:04:17 +01:00 
			
		
		
		
	Compare commits
	
		
			345 Commits
		
	
	
		
			fix/databa
			...
			v0.8.18
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f503e140f3 | ||
|   | f875f3cd20 | ||
|   | 72632c7b45 | ||
|   | 1f2e7bd72d | ||
|   | 016138ab55 | ||
|   | 77efb80a12 | ||
|   | 02eea950e4 | ||
|   | 2793c41655 | ||
|   | 62d3c198d5 | ||
|   | ef62b53cec | ||
|   | 7181b988e9 | ||
|   | 8b0b123f42 | ||
|   | f98332c698 | ||
|   | b1660bf27a | ||
|   | 191ad25eb2 | ||
|   | 7469be6efb | ||
|   | 47a3bfd265 | ||
|   | 5ae0be2323 | ||
|   | 42154d55a0 | ||
|   | 9dcc08ee6e | ||
|   | ba537b42bb | ||
|   | 9c318f9012 | ||
|   | a88103a956 | ||
|   | e26e6f3a1c | ||
|   | 08d67a07e0 | ||
|   | 6e9a89cb2e | ||
|   | e3aa3d4031 | ||
|   | 156a68f8bd | ||
|   | 6a28da5d33 | ||
|   | ee32384dca | ||
|   | 19afd73e8a | ||
|   | eead6a2975 | ||
|   | 11b11dd3ee | ||
|   | 483155cf4c | ||
|   | 55dbade8d5 | ||
|   | d1bc63d07a | ||
|   | 83a91ec540 | ||
|   | 7b43c972dd | ||
|   | 3fdf1d3f44 | ||
|   | 764db0c203 | ||
|   | 538076bc45 | ||
|   | 4327b66f91 | ||
|   | 3f27724569 | ||
|   | 9c0c2bbece | ||
|   | 946144016b | ||
|   | 5c3b01aca4 | ||
|   | 50983a2b85 | ||
|   | 765886f0c7 | ||
|   | 8ea78a5852 | ||
|   | 063cbab93e | ||
|   | f1670f4012 | ||
|   | 6fcf0f7117 | ||
|   | 7a07763625 | ||
|   | 8d2172a649 | ||
|   | b8f65207b6 | ||
|   | ea675afe86 | ||
|   | cc1cebf359 | ||
|   | 1e874c7027 | ||
|   | a338fd967c | ||
|   | 8796ff7e72 | ||
|   | a8172a518f | ||
|   | 559c2c7bd2 | ||
|   | 7a546e5a41 | ||
|   | 5f7a9154b7 | ||
|   | 0d52978d9f | ||
|   | df48363ea4 | ||
|   | 34725e0257 | ||
|   | df321154f6 | ||
|   | f2a3ec1185 | ||
|   | ce4aed0f17 | ||
|   | d0c82f6fb0 | ||
|   | 89b76e721d | ||
|   | 6091290efe | ||
|   | e4c4990e7f | ||
|   | 4f1235bfe9 | ||
|   | bf9d440a95 | ||
|   | 59a149c253 | ||
|   | 563099f290 | ||
|   | 3732bf988d | ||
|   | ab964fa1de | ||
|   | b5c694224e | ||
|   | 23810345f6 | ||
|   | b574a29331 | ||
|   | 6593e32d83 | ||
|   | b09adc836d | ||
|   | dcf9a45974 | ||
|   | 21258313ef | ||
|   | f851fa3845 | ||
|   | 2fdd507902 | ||
|   | b850e56ff1 | ||
|   | 8d10082179 | ||
|   | 996ae625c9 | ||
|   | 467f7ef927 | ||
|   | fa015b8d39 | ||
|   | 4b4864561c | ||
|   | 475cea8549 | ||
|   | 760214fdbd | ||
|   | 9c5dcd7b19 | ||
|   | 720e400f7c | ||
|   | c261c6ba48 | ||
|   | 933d4e1d6f | ||
|   | 1d08833726 | ||
|   | 7cfca55e3c | ||
|   | 7e13b62b8f | ||
|   | 5ccfcd541e | ||
|   | ba4d2a9c48 | ||
|   | 7a61f613ec | ||
|   | 073d9da0bc | ||
|   | e081a67589 | ||
|   | 37a27b8065 | ||
|   | d34f7eb2fc | ||
|   | f6c080cb5c | ||
|   | a983fd03b1 | ||
|   | 5b651886a5 | ||
|   | 6484ab75d9 | ||
|   | becad18465 | ||
|   | ec7e5a8838 | ||
|   | 300c1b4ebc | ||
|   | 4f0dd89eb9 | ||
|   | 79f7c3dbd4 | ||
|   | f24fc75b12 | ||
|   | 4fcc71e127 | ||
|   | 403b4aa89b | ||
|   | 613bcf379b | ||
|   | 8f2cc2ca38 | ||
|   | cdae11226d | ||
|   | 79fd97ad78 | ||
|   | b477d4c821 | ||
|   | 289f82ad23 | ||
|   | 92569b7965 | ||
|   | c03b041f40 | ||
|   | 2ac8ed7411 | ||
|   | 3280b3b9df | ||
|   | 05c37b94bb | ||
|   | a91e7226dc | ||
|   | a39600efe2 | ||
|   | 4fd5922784 | ||
|   | b3d1274ffe | ||
|   | 9de49b4b6a | ||
|   | e6a368c3ac | ||
|   | 3dca1b519a | ||
|   | ae8af396f4 | ||
|   | 38294420d5 | ||
|   | c0dee61add | ||
|   | a84c92e007 | ||
|   | 60a854e5fd | ||
|   | fa9f7aab7c | ||
|   | d697a556c2 | ||
|   | 802fd831cb | ||
|   | 52ca8a844a | ||
|   | 573fc819d3 | ||
|   | ead9d79cb1 | ||
|   | 43c197316a | ||
|   | c318d3a9de | ||
|   | 80d3f5ffe8 | ||
|   | f9d85b10b7 | ||
|   | 4028a58adc | ||
|   | 0a3a079b86 | ||
|   | d691148c84 | ||
|   | 48f74b9c7a | ||
|   | 99338afacb | ||
|   | 301c3b86e2 | ||
|   | 130f7ba405 | ||
|   | e552bcfe82 | ||
|   | ad75499a91 | ||
|   | 7ddcb441c8 | ||
|   | 99da8dbec1 | ||
|   | 743d5164b3 | ||
|   | 1f7dfdca50 | ||
|   | 2164d59331 | ||
|   | 0d845b764b | ||
|   | 0a2cd69a4b | ||
|   | ddb60fa1e0 | ||
|   | b7e33c5e6d | ||
|   | 095ff7963d | ||
|   | dfb8be07f0 | ||
|   | 74b4d98bba | ||
|   | 8a7e4f0132 | ||
|   | 6ebdb0ba89 | ||
|   | 33817e5ab1 | ||
|   | cd1b481de5 | ||
|   | ab99db62bd | ||
|   | dd3d6b3f47 | ||
|   | d99f507244 | ||
|   | b6c8a0923f | ||
|   | 0663b3bbf4 | ||
|   | d683fce2b9 | ||
|   | 3d56214c07 | ||
|   | e2575c3612 | ||
|   | 084256ce01 | ||
|   | 240be18367 | ||
|   | ac271839a6 | ||
|   | 734a63f6c3 | ||
|   | e73daedd42 | ||
|   | 802e6c228c | ||
|   | edf2004539 | ||
|   | 9e949eb32a | ||
|   | e255c35002 | ||
|   | dfd3c2c4a5 | ||
|   | 876543f064 | ||
|   | e10f82e0e9 | ||
|   | f496a48a4d | ||
|   | 1fbb7c373e | ||
|   | d58d7f79e7 | ||
|   | 0643c56bc5 | ||
|   | c51acf9dbd | ||
|   | 2f2a326eac | ||
|   | 10dad0fb4e | ||
|   | fe01070bd1 | ||
|   | b558109844 | ||
|   | ac37802b7a | ||
|   | f9c8027c3f | ||
|   | 28dd1969b3 | ||
|   | 50270f6d6f | ||
|   | fad80307d8 | ||
|   | 323d1b0504 | ||
|   | d6c1232d2d | ||
|   | 678d719036 | ||
|   | 723e81e90e | ||
|   | 60d2cfea99 | ||
|   | 84c39676ee | ||
|   | a11d038c1d | ||
|   | 2f627bf4b0 | ||
|   | 67c603ef94 | ||
|   | dec63f353f | ||
|   | 7ab6b026fb | ||
|   | a2a773d714 | ||
|   | aa26a71949 | ||
|   | e3c695175b | ||
|   | ccdcdd01d8 | ||
|   | ef2a408298 | ||
|   | 6a4dbcef62 | ||
|   | c8a87a2a8a | ||
|   | d0bfe93d38 | ||
|   | 75df8c5a6a | ||
|   | b55813e362 | ||
|   | 575f0c62b4 | ||
|   | 3e9544f1dc | ||
|   | 152e5ac70e | ||
|   | 3544643a07 | ||
|   | f7ae76d10f | ||
|   | add9370696 | ||
|   | cadc17cc52 | ||
|   | 878dfc5a01 | ||
|   | 2400f7c20a | ||
|   | 9d7854dda6 | ||
|   | 7303aabc2b | ||
|   | ab478f488a | ||
|   | c7a2b090d7 | ||
|   | f2c3b6362d | ||
|   | c664be7b74 | ||
|   | aa17ddbf24 | ||
|   | 62faacbed6 | ||
|   | d03d6e2ec1 | ||
|   | 93a1760263 | ||
|   | 0de8894e4d | ||
|   | e95ea22dbd | ||
|   | 5dbfde62a6 | ||
|   | 0ba886bc6b | ||
|   | 2684205b5a | ||
|   | 856507fb66 | ||
|   | d019fbc050 | ||
|   | 129d5653e6 | ||
|   | 9560ad0c20 | ||
|   | a4fa7567aa | ||
|   | 9ff032740c | ||
|   | dbc76abcdd | ||
|   | 746a5dc816 | ||
|   | 194d110079 | ||
|   | eed490507c | ||
|   | 2800ab1e88 | ||
|   | faf40f8bed | ||
|   | 3f12ee40ac | ||
|   | b4532bd473 | ||
|   | b389874a7e | ||
|   | ea2dc69f7c | ||
|   | b0593b0ae1 | ||
|   | 664348e475 | ||
|   | e3e00f0109 | ||
|   | d3850280f4 | ||
|   | d8b3b41358 | ||
|   | e7706cad74 | ||
|   | 727d6788fe | ||
|   | e3ecf8ec96 | ||
|   | 1999b19a1c | ||
|   | c55e2910ac | ||
|   | ad3694ef96 | ||
|   | 7fd784b2f6 | ||
|   | d8f0ac5880 | ||
|   | b4ee4de110 | ||
|   | b9713fea76 | ||
|   | c6dd937922 | ||
|   | 81d4158c03 | ||
|   | bf0e6ff862 | ||
|   | 8518afe70d | ||
|   | 091ab688e7 | ||
|   | 2e5b066d88 | ||
|   | cd3a15feab | ||
|   | 836499a48c | ||
|   | 4983816ff6 | ||
|   | 0326657d60 | ||
|   | fcff820858 | ||
|   | dc536f0fc0 | ||
|   | f958b8dd4f | ||
|   | 352d3b1bde | ||
|   | f3061a0e74 | ||
|   | d4222e98e6 | ||
|   | 8dd07d4873 | ||
|   | 5f891fb9d6 | ||
|   | b3b6d0ff5d | ||
|   | 0ac01b3f0d | ||
|   | c6e55edb09 | ||
|   | dfc70c098f | ||
|   | c1d1e9596a | ||
|   | 53078e046e | ||
|   | 2af968031c | ||
|   | bdd3c394a3 | ||
|   | f719b3c112 | ||
|   | 8e0001a635 | ||
|   | 5b3581b88e | ||
|   | ca1436ea3f | ||
|   | 5cd6b79ee0 | ||
|   | 0ec5942ee4 | ||
|   | 90af164a8a | ||
|   | fd83ee37ae | ||
|   | 8e935e62be | ||
|   | a0d38b1f36 | ||
|   | 6b49ecc99a | ||
|   | ae24a464dc | ||
|   | f519784cae | ||
|   | 601cbd5ab7 | ||
|   | fd4a507645 | ||
|   | bf8d07226e | ||
|   | fcddfb6adf | ||
|   | 5fcd97129e | ||
|   | af3a1c2f55 | ||
|   | 13584a4d96 | ||
|   | 6de4953d50 | ||
|   | b15c213890 | ||
|   | 2f52e5d9f3 | ||
|   | 1d6afea9c4 | ||
|   | 8d79efd6ad | ||
|   | 99bcd54afe | ||
|   | 6ce6eee529 | ||
|   | 054b40e358 | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -8,3 +8,4 @@ _config.yml export-ignore | |||||||
| release.sh export-ignore | release.sh export-ignore | ||||||
|  |  | ||||||
| *.sh text eol=lf | *.sh text eol=lf | ||||||
|  | VERSION text eol=lf | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								.github/workflows/phplint.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/phplint.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | name: PHP Linting | ||||||
|  | on: | ||||||
|  |   pull_request: | ||||||
|  |     branches: [master] | ||||||
|  |   push: | ||||||
|  |     branches: [master] | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   phplint: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v3 | ||||||
|  |       - uses: overtrue/phplint@3.4.0 | ||||||
|  |         with: | ||||||
|  |           path: . | ||||||
|  |           options: --exclude="system/libs/polyfill-mbstring/bootstrap80.php" | ||||||
							
								
								
									
										41
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,11 +1,28 @@ | |||||||
| Thumbs.db | Thumbs.db | ||||||
| .DS_Store | .DS_Store | ||||||
| .idea | .idea | ||||||
|  |  | ||||||
|  | # | ||||||
|  | /.htaccess | ||||||
|  |  | ||||||
|  | # composer | ||||||
|  | composer.lock | ||||||
|  | vendor | ||||||
|  |  | ||||||
|  | # npm | ||||||
|  | node_modules | ||||||
|  | tools/ext | ||||||
|  |  | ||||||
|  | # cypress | ||||||
|  | cypress.env.json | ||||||
|  | cypress/e2e/2-advanced-examples | ||||||
|  | cypress/screenshots | ||||||
|  |  | ||||||
|  | # created by release.sh | ||||||
|  | releases | ||||||
| tmp | tmp | ||||||
|  |  | ||||||
| releases |  | ||||||
| config.local.php | config.local.php | ||||||
| PERSONAL_NOTES |  | ||||||
|  |  | ||||||
| # all custom templates | # all custom templates | ||||||
| templates/* | templates/* | ||||||
| @@ -16,6 +33,16 @@ templates/* | |||||||
| images/guilds/* | images/guilds/* | ||||||
| !images/guilds/default.gif | !images/guilds/default.gif | ||||||
|  |  | ||||||
|  | # editor images | ||||||
|  | images/editor/* | ||||||
|  | !images/editor/index.html | ||||||
|  |  | ||||||
|  | # gallery images | ||||||
|  | images/gallery/* | ||||||
|  | !images/gallery/index.html | ||||||
|  | !images/gallery/demon.jpg | ||||||
|  | !images/gallery/demon_thumb.gif | ||||||
|  |  | ||||||
| # cache | # cache | ||||||
| system/cache/* | system/cache/* | ||||||
| !system/cache/index.html | !system/cache/index.html | ||||||
| @@ -23,16 +50,26 @@ system/cache/* | |||||||
| !system/cache/signatures/index.html | !system/cache/signatures/index.html | ||||||
| !system/cache/plugins/index.html | !system/cache/plugins/index.html | ||||||
|  |  | ||||||
|  | # php sessions | ||||||
|  | system/php_sessions/* | ||||||
|  | !system/php_sessions/index.html | ||||||
|  |  | ||||||
| # logs | # logs | ||||||
| system/logs/* | system/logs/* | ||||||
| !system/logs/index.html | !system/logs/index.html | ||||||
|  |  | ||||||
|  | # data | ||||||
|  | system/data/* | ||||||
|  | !system/data/index.html | ||||||
|  |  | ||||||
| # plugins | # plugins | ||||||
| plugins/* | plugins/* | ||||||
| !plugins/.htaccess | !plugins/.htaccess | ||||||
| !plugins/example.json | !plugins/example.json | ||||||
| !plugins/account-create-hint.json | !plugins/account-create-hint.json | ||||||
| !plugins/account-create-hint | !plugins/account-create-hint | ||||||
|  | !plugins/email-confirmed-reward.json | ||||||
|  | !plugins/email-confirmed-reward | ||||||
| landing | landing | ||||||
|  |  | ||||||
| # others/rest | # others/rest | ||||||
|   | |||||||
| @@ -7,13 +7,14 @@ php: | |||||||
|   - 7.2 |   - 7.2 | ||||||
|   - 7.3 |   - 7.3 | ||||||
|   - 7.4 |   - 7.4 | ||||||
|  |   - 8.0 | ||||||
|  |  | ||||||
| cache: | cache: | ||||||
|   directories: |   directories: | ||||||
|     - $HOME/.composer/cache |     - $HOME/.composer/cache | ||||||
|  |  | ||||||
| before_script: | before_script: | ||||||
|   - composer require jakub-onderka/php-parallel-lint --no-suggest --no-progress --no-interaction --no-ansi --quiet --optimize-autoloader |   - composer require php-parallel-lint/php-parallel-lint --no-suggest --no-progress --no-interaction --no-ansi --quiet --optimize-autoloader | ||||||
|  |  | ||||||
| script: | script: | ||||||
|   - php vendor/bin/parallel-lint --no-progress --no-colors --exclude vendor .  |   - php vendor/bin/parallel-lint --no-progress --no-colors --exclude vendor --exclude "system/libs/pot/OTS_DB_PDOQuery_PHP71.php" . | ||||||
|   | |||||||
							
								
								
									
										277
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										277
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,19 +1,288 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
| ## [0.8.2 - x.x.2020] | ## [0.8.18 - 29.05.2024] | ||||||
|  |  | ||||||
| ### Added | ### Added | ||||||
|  | * hook in guilds page to support guild wars (https://github.com/slawkens/myaac/commit/f875f3cd2059fac5c23a08ce73dd8621a66613e0) | ||||||
|  |  | ||||||
|  | ## [0.8.17 - 18.05.2024] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * TwigTypeCastingExtension (https://github.com/slawkens/myaac/commit/7181b988e9518320d57486670ca4e2d3b2fe1cfa) | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * fix XSS in creatures.php (https://github.com/slawkens/myaac/commit/02eea950e4fd756e8d5c32e56181986d51f5ac70, @gesior) | ||||||
|  | * don't allow redirect to external website (https://github.com/slawkens/myaac/commit/ef62b53cec5a479cc85aa15940ad9ebbcefde876) | ||||||
|  | * change_info if account_country is disabled (https://github.com/slawkens/myaac/commit/62d3c198d567541a90900fe2d7ede070e7b1ff68) | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * use word-break: break-all in guilds description + character comment (https://github.com/slawkens/myaac/commit/191ad25eb2d4c1cec6f6668da7a345fec0ad2a7f) | ||||||
|  | * set default status_ip to 127.0.0.1, most server are hosted locally anyway (https://github.com/slawkens/myaac/commit/2793c41655b47f7db295143a298ccda70f11462b) | ||||||
|  |  | ||||||
|  | ## [0.8.16 - 12.02.2024] | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * broken installation | ||||||
|  | * database and finish step warnings/errors (https://github.com/slawkens/myaac/pull/245, @danilopucci) | ||||||
|  | * silently ignore if the hook does not exist | ||||||
|  |  | ||||||
|  | ## [0.8.15 - 09.12.2023] | ||||||
|  |  | ||||||
|  | More security fixes, especially in bugtracker. | ||||||
|  |  | ||||||
|  | ## [0-8.14 - 27.11.2023] | ||||||
|  | Security fixes. | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * XSS vulnerability in bugtracker (https://github.com/slawkens/myaac/commit/83a91ec540072d319dd338abff45f8d5ebf48190) | ||||||
|  | * XSS vulnerability in forum (https://github.com/slawkens/myaac/commit/d1bc63d07ad88a143358cacd2c417891eea74dcc + https://github.com/slawkens/myaac/commit/55dbade8d5280c5baed45e5f7ebc3613b8e9b9e8) | ||||||
|  | * Session Fixation (https://github.com/slawkens/myaac/commit/483155cf4c1e3068aaee0d44541dfa61f6223379) | ||||||
|  | * displaying ban info on account page (https://github.com/slawkens/myaac/commit/764db0c203d1826ffce3a5a78f83a97e56bd0685) | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * Clear some additional cache keys - like database cache (https://github.com/slawkens/myaac/commit/4327b66f915d06dce504211692173606b9ef3b4e) | ||||||
|  |  | ||||||
|  | ## [0.8.13 - 16.09.2023] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * latest client versions to config (https://github.com/slawkens/myaac/commit/765886f0c782807400c429577cde5e45bd7c308f) | ||||||
|  | * patching from develop - twig context for hooks (https://github.com/slawkens/myaac/commit/f1670f4012cc7595433fe0b1937c1f9b15a60b07) | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * fixed XSS vulnerability in some pages (https://github.com/slawkens/myaac/commit/5c3b01aca4f3cfe8abc86b8ce48194b2da87b808) | ||||||
|  |  | ||||||
|  | Nothing more or less! | ||||||
|  |  | ||||||
|  | ## [0.8.12 - 07.08.2023] | ||||||
|  | I've moved the repository back to my personal account. (Just so you know!) | ||||||
|  |  | ||||||
|  | I will also try to add git commits pointed to each change, lets see if you like it or not - you can comment in discussion, that will be created just after releasing this version :) | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * forum: better error messages (Suggested by @anyeor) (https://github.com/slawkens/myaac/commit/34725e0257684fe5fa43875cc3a8f587ba04642e) | ||||||
|  | * more support for GesiorAAC classes, so some of them will work with MyAAC (https://github.com/slawkens/myaac/commit/a8172a518ff8939c4402349b16c064fcaf855d31) | ||||||
|  | * word-break on forum thread & reply (Suggested by @anyeor) (https://github.com/slawkens/myaac/commit/ce4aed0f1719d2aadc749e5238e883e3c10e2686) | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * not working pages/links from database, introduced in 0.8.10 (Thanks to OtLand user - https://otland.net/members/0lo.99657/ for report) (https://github.com/slawkens/myaac/commit/1e874c7027769bd09e772a1cdac75d7e37991256) | ||||||
|  | * it was possible to create topic in board that was closed, ommiting the error check (Thanks to @anyeor for report) (https://github.com/slawkens/myaac/commit/0d52978d9fb99869500d35e7676f454ca5eaba14) | ||||||
|  | * PHP 8.2 compatibility - removed deprecated functions utf8_encode & utf8_decode (https://github.com/slawkens/myaac/commit/a338fd967cdbcc89e86be4e6b66b2cad2ff23251) | ||||||
|  | * guild description not being correctly shown (Reported by @anyeor)  (https://github.com/slawkens/myaac/commit/f2a3ec1185df64ad9084d4ff55790ae4a5b3e5fd, https://github.com/slawkens/myaac/commit/df321154f63d458a4bc7d83bac5e3447b67317a4) | ||||||
|  |  | ||||||
|  | ### Removed | ||||||
|  | * Some old code for verifying messages length (Reported by @anyeor) (https://github.com/slawkens/myaac/commit/df48363ea4ced4350fd90ffddf57d464ba5afa8b) | ||||||
|  | * some info about config failed to load, was never working (https://github.com/slawkens/myaac/commit/7a546e5a41036b0e9e926d337c6f2e3c41c591d2) | ||||||
|  |  | ||||||
|  | ## [0.8.11 - 30.06.2023] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * new function from 0.9 - Cache::remember($key, $ttl, $callback) | ||||||
|  | * new characters page hooks | ||||||
|  | * line number & file to exception handler, to easier localize exceptions | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * rename to .htaccess.dist, causes some problems on default setup | ||||||
|  | * removing unneccessary PHP closing tags to prevent potential issues (by @SRNT-GG) | ||||||
|  | * display warning if hook file does not exist | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * important: Not allow create char if limit is exceeded (by @anyeor) could have been used to spam database | ||||||
|  | * deleted chars: cannot change comment, name, gender, cannot create guild, cannot be invited, cannot accept invite, cannot be passed leadership to | ||||||
|  | * forum: quote and edit post buttons not being shown | ||||||
|  | * twig exception thrown when player does not exist, on character change comment (thanks @anyeor) | ||||||
|  | * BASE_DIR when accessing /tools | ||||||
|  | * do not display warning if HTTP_ACCEPT_LANGUAGE is not set | ||||||
|  |  | ||||||
|  | ## [0.8.10 - 18.05.2023] | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * PHP 7.2.5 is now required, cause of Twig 2.x | ||||||
|  | * allow pages to be placed in templates folder, under pages/ subfolder | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * Twig error with global variable on create account | ||||||
|  | * links/redirects from facebook, etc. like ?fbclid=x | ||||||
|  | * do not allow to continue install when there is no server database imported | ||||||
|  | * cannot go forward when config.local.php cannot be saved | ||||||
|  | * when server uses another items serializer | ||||||
|  | * small bug on install - please fill all input | ||||||
|  |  | ||||||
|  | ## [0.8.9 - 16.03.2023] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * You can now disable server status checking for testing purposes, useful for local testing when there is no server running | ||||||
|  |   * with this, the page won't need 2 seconds to load | ||||||
|  |   * set status_enabled to false in config.php | ||||||
|  | * new buttons code for tibiacom template, can create button with any text | ||||||
|  | * patched some small changes from develop branch | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * add .git to denied folders in nginx-sample.conf | ||||||
|  | * plugins folder is now accessible from outside | ||||||
|  | * add plugins folder to twig search paths | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * player save on tfs 1.5 with new ipv6 | ||||||
|  | * more php 8.x compatibility | ||||||
|  | * rel path for exception message, causing message to be not in red background | ||||||
|  |  | ||||||
|  | ## [0.8.8 - 18.02.2023] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * mail confirmed reward | ||||||
|  | * support for latest group changes in TFS | ||||||
|  | * new function: escapeHtml | ||||||
|  |  | ||||||
|  | ### Updated | ||||||
|  | * TinyMCE to v4.9.1 (latest release in 4.x series) | ||||||
|  | * Twig to v2.15.4 | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * you can now place custom pages in your template directory under pages/ folder | ||||||
|  | * HOOK_LOGOUT parameters, now only account_id is passed | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * ipv6 introduced in latest TFS | ||||||
|  | * config.account_premium_days for TFS 1.4+ | ||||||
|  | * better compatibility with GesiorAAC | ||||||
|  | * PHP 8.1 compatibility | ||||||
|  | * myaac_ db table detection failure | ||||||
|  | * reload creatures error, when items cache has been cleared | ||||||
|  |  | ||||||
|  | ### Removed | ||||||
|  | * accounts.blocked column, which is not used by AAC | ||||||
|  |  | ||||||
|  | ## [0.8.7 - 31.08.2022] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * login.php for client 12.x is now part of official repo | ||||||
|  | * browsehappy code | ||||||
|  | * config use character sample skill (#201, @gpedro) | ||||||
|  | * custom words blocked (#190, @gpedro) | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * save php sessions in myaac dir | ||||||
|  | * don't count deleted players when creating new character | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * patch vulnerability in change_rank.php (#194, @gesior, @thatmichaelguy) | ||||||
|  | * fix guild invite page (#196, @worthdavi) | ||||||
|  | * players not showing on highscores page (#195) | ||||||
|  | * highscores page bug with high pages | ||||||
|  | * $player->getStorage() does not work at all (#169, @gesior) | ||||||
|  | * copying sample character when it have items with quotes (#200, @gpedro) | ||||||
|  | * IPv6 issue when env is set to dev (#171) | ||||||
|  | * admin page changed feet to match body colour (#174, @silic0nalph4) | ||||||
|  | * exception being thrown when creating duplicated character name (#191) | ||||||
|  | * rules page formatting (#177, @silic0nalph4) | ||||||
|  | * account character create if auto_login is enabled | ||||||
|  | * undefined variable notice on database_log enabled | ||||||
|  | * removed VERSION file | ||||||
|  |  | ||||||
|  | ## [0.8.6 - 10.07.2021] | ||||||
|  | This update contains very important security fix. | ||||||
|  |  | ||||||
|  | Please update your MyAAC instances to this version. | ||||||
|  |  | ||||||
|  | ## [0.8.5 - 08.06.2021] | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * bcmath module is not required anymore | ||||||
|  | * Gratis premium account fixes (#156, by @czbadaro) | ||||||
|  | * Update 404 response (#163, by @anyeor) | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * compatibility with PHP 7.0 and lower | ||||||
|  | * deleting ranks in guilds (#158, by @Misztrz) | ||||||
|  | * guild back buttons (change logo & motd) | ||||||
|  | * forum table style (boards & thread view) | ||||||
|  | * guild list description new lines `<br>` being ignored (Thanks @anyeor for reporting) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## [0.8.4 - 18.02.2021] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * support for accounts.premium_ends_at (Latest TFS 1.x) | ||||||
|  | * more clients to clients.conf.php | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * minimum PHP 5.6 is now required | ||||||
|  | * password can now contain any characters | ||||||
|  | * add SSL on external image requests of items and outfits (@fernandomatos) | ||||||
|  | * Use local storage for saving menu items (tibiacom template) - fixes bug with some websites like wykop.pl (browser freeze) | ||||||
|  | * increase size of myaac_visitors.page column to 2048 (Thanks to OtLand user kaleuui) | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * compatibility with PHP 8.0 (latest XAMPP) | ||||||
|  | * displaying PHP errors on env = "prod" | ||||||
|  | * the Guildnick not showing in the guild pages (@leesneaks) | ||||||
|  | * you cannot delete character more than twice (Thanks Okke) | ||||||
|  | * ignore arrays in config.lua (fixes experienceStages loading) | ||||||
|  | * parsing empty strings in config.lua (with comments) | ||||||
|  | * headling.php cannot find font | ||||||
|  |  | ||||||
|  | ## [0.8.3 - 27.10.2020] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * pdo_mysql as required extension | ||||||
|  | * some notice about Email validation in create account | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * Move register DATABASE_VERSION into schema.sql | ||||||
|  |     * Caused migrations being fired when user manually imported database | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * creating very uncommon (bugged) account names | ||||||
|  | * XSS in character search | ||||||
|  | * Admin menu news editing warning when leaving page without touching the inputs | ||||||
|  | * Guild Invite not working on otservbr-global | ||||||
|  | * two boxes being show on email_change_cancel | ||||||
|  | * when adding poll = template tibiacom broken | ||||||
|  | * houses: Unknown column 'guild' in 'where clause (https://github.com/slawkens/myaac/issues/131) | ||||||
|  | * account create when account_mail_verify is enabled | ||||||
|  | * CloudFlare IP detection | ||||||
|  | * network_twitter link in tibiacom template | ||||||
|  |  | ||||||
|  | ## [0.8.2 - 03.06.2020] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * Log query time in database_log (can be used for benchmarking) | ||||||
|  | * new PHP constant: IS_CLI | ||||||
| * $_SERVER['REQUEST_URI'] to database.log | * $_SERVER['REQUEST_URI'] to database.log | ||||||
|  | * outfit to highscores box in tibiacom template | ||||||
|  | * system/data to .gitignore | ||||||
|  | * error_reporting in admin panel (when in dev mode), so it shows php notices and warnings | ||||||
|  | * example quests in config.php | ||||||
|  |  | ||||||
| ### Changed | ### Changed | ||||||
| * account_login input type from password to text | * account_login input type from password to text | ||||||
|  |  | ||||||
| ### Fixed | ### Fixed | ||||||
| * Updating template menus on template change | * Guild Invite not working on otservbr-global (#123) | ||||||
|  | * news not updating after adding in admin panel | ||||||
|  | * wrong mana of character samples (#125) | ||||||
|  | * missing rules page on clean install | ||||||
|  | * double space character name creation (@Lee, #121) | ||||||
|  | * creatures page: Max count and chance not shown on hovered items | ||||||
|  | * exception being thrown when characters.frags enabled on TFS 1.x | ||||||
|  | * TFS 0.4 guilds creation (Where guilds.checkdata and motd doesn't have default value) | ||||||
|  | * ERR_TOO_MANY_REDIRECTS browser error on template change | ||||||
|  | * updating template menus on template change | ||||||
| * Account change info when config.account_country is disabled | * Account change info when config.account_country is disabled | ||||||
|  | * cancel change email request | ||||||
|  | * config.character_name_min/max_length being ignored in change_name.php | ||||||
|  | * some rare bugs when database is no up-to-date and someone enters admin panel | ||||||
|  | * extra line that is added when using a newer version than official release (@Lee) | ||||||
|  | * admin links in featured article | ||||||
|  | * some PHP Notice when HTTP_HOST is not set (Can happen on some old versions of HTTP protocol) | ||||||
| * Show character indicator in check_name.js | * Show character indicator in check_name.js | ||||||
| * Houses list View button | * Houses list View button was wrong (was from bootstrap) | ||||||
| * Fix OTS_House houseid parameter | * OTS_House __construct - not loading by houseid parameter | ||||||
|  | * message() function when executed in CLI | ||||||
|  |  | ||||||
|  | ### Removed | ||||||
|  | * unused myaac_commands table from schema | ||||||
|  | * MyISAM engine from migration scripts (#128) | ||||||
|  |  | ||||||
| ## [0.8.1 - 10.03.2020] | ## [0.8.1 - 10.03.2020] | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								CONTRIBUTORS.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								CONTRIBUTORS.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # automatically exported using this script: | ||||||
|  | # git log --all --format='%cN <%cE>' | sort -u > contributors | ||||||
|  | # in no particular order | ||||||
|  | # cleaned for readability | ||||||
|  |  | ||||||
|  | Evil Puncker <EPuncker@users.noreply.github.com> | ||||||
|  | Fernando Matos <fernando@pixele.com.br> | ||||||
|  | Lee <42119604+Leesneaks@users.noreply.github.com> | ||||||
|  | caio <caio.zucoli@gmail.com> | ||||||
|  | slawkens <slawkens@gmail.com> | ||||||
|  | tobi132 <52947952+tobi132@users.noreply.github.com> | ||||||
|  | vankk <nwtr.otland@hotmail.com> | ||||||
|  | whiteblXK <krzys16001@gmail.com> | ||||||
|  | xitobuh <jonas.hockert92@gmail.com> | ||||||
							
								
								
									
										3
									
								
								CREDITS
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								CREDITS
									
									
									
									
									
								
							| @@ -1,2 +1,3 @@ | |||||||
| * Gesior.pl (2007 - 2008) | * Gesior.pl (2007 - 2008) | ||||||
| * Slawkens (2009 - 2020) | * Slawkens (2009 - 2023) | ||||||
|  | * Contributors listed in CONTRIBUTORS.txt | ||||||
|   | |||||||
							
								
								
									
										77
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,18 +1,31 @@ | |||||||
| # myaac | # [MyAAC](https://my-aac.org) | ||||||
|  |  | ||||||
| MyAAC is a free and open-source Automatic Account Creator (AAC) written in PHP. It is a fork of the [Gesior](https://github.com/gesior/Gesior2012) project. It supports only MySQL databases. | MyAAC is a free and open-source Automatic Account Creator (AAC) written in PHP. It is a fork of the [Gesior](https://github.com/gesior/Gesior2012) project. It supports only MySQL databases. | ||||||
|  |  | ||||||
| Official website: https://my-aac.org | Official website: https://my-aac.org | ||||||
|  |  | ||||||
| ### REQUIREMENTS | [](https://github.com/slawkens/myaac/actions) | ||||||
|  | [](https://opensource.org/licenses/gpl-license) | ||||||
|  | [](https://github.com/slawkens/myaac/releases) | ||||||
|  | [](https://discord.gg/2J39Wus) | ||||||
|  | [](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed) | ||||||
|  |  | ||||||
|  | | Version | Status                 | Branch  | Requirements   | | ||||||
|  | |:--------|:-----------------------|:--------|:---------------| | ||||||
|  | | **1.x** | **Active development** | develop | **PHP >= 8.1** | | ||||||
|  | | 0.9.x   | Not developed anymore  | 0.9     | PHP >= 7.2.5   | | ||||||
|  | | 0.8.x   | Active support         | master  | PHP >= 7.2.5   | | ||||||
|  | | 0.7.x   | End Of Life            | 0.7     | PHP >= 5.3.3   | | ||||||
|  |  | ||||||
|  | ### Requirements | ||||||
|  |  | ||||||
| 	- PHP 5.5 or later |  | ||||||
| 	- MySQL database | 	- MySQL database | ||||||
| 	- PDO PHP Extension | 	- PHP Extensions: pdo, xml, json | ||||||
| 	- XML PHP Extension | 	- (optional) apache2 mod_rewrite (to use friendly_urls) | ||||||
| 	- ZIP PHP Extension | 	- (optional) zip PHP Extension (to install plugins) | ||||||
| 	- (optional) mod_rewrite to use friendly_urls | 	- (optional) gd PHP Extension (for generating signature images) | ||||||
|  |  | ||||||
| ### INSTALLATION AND CONFIGURATION | ### Installation | ||||||
|  |  | ||||||
| 	Just decompress and untar the source (which you should have done by now, | 	Just decompress and untar the source (which you should have done by now, | ||||||
| 	if you're reading this), into your webserver's document root. | 	if you're reading this), into your webserver's document root. | ||||||
| @@ -28,19 +41,51 @@ Official website: https://my-aac.org | |||||||
| 			chmod 660 images/guilds | 			chmod 660 images/guilds | ||||||
| 			chmod 660 images/houses | 			chmod 660 images/houses | ||||||
| 			chmod 660 images/gallery | 			chmod 660 images/gallery | ||||||
| 			chmod -R 770 system/cache | 			chmod -R 760 system/cache | ||||||
|  |  | ||||||
| 	Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser. | 	Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser. | ||||||
|  |  | ||||||
| ### KNOWN PROBLEMS | ### Configuration | ||||||
|  |  | ||||||
| 	- none - | Check *config.php* to get more informations. (Notice: MyAAC 1.0+ doesn't use config.php anymore, it has been moved to Admin Panel - Settings page). | ||||||
|  |  | ||||||
| ### OTHER NOTES | Use *config.local.php* for your local configuration changes. | ||||||
|  |  | ||||||
| 	If you have a great idea or want contribute to the project - visit our website at https://www.my-aac.org | ### Branches | ||||||
|  |  | ||||||
| ### LICENSING | This repository follows the Git Flow Workflow. | ||||||
|  | Cheatsheet: [Git-Flow-Cheetsheet](https://danielkummer.github.io/git-flow-cheatsheet) | ||||||
|  |  | ||||||
| 	This program and all associated files are released under the GNU Public | That means, we use: | ||||||
| 	License, see LICENSE for details. | * master branch, for current stable release | ||||||
|  | * develop branch, for development version (next release) | ||||||
|  | * feature branches, for features etc. | ||||||
|  |  | ||||||
|  | ### Known Problems | ||||||
|  |  | ||||||
|  | - Some compatibility issues with some exotical distibutions. | ||||||
|  |  | ||||||
|  | ### Contributing | ||||||
|  |  | ||||||
|  | Contributions are more than welcome.  | ||||||
|  |  | ||||||
|  | Pull requests should be made to the *develop* branch as that is the working branch, master is for release code.   | ||||||
|  |  | ||||||
|  | Bug fixes to current release should be done to master branch. | ||||||
|  |  | ||||||
|  | Look: [Contributing](https://github.com/otsoft/myaac/wiki/Contributing) in our wiki. | ||||||
|  |  | ||||||
|  | ### Other Notes | ||||||
|  |  | ||||||
|  | If you have a great idea or want contribute to the project - visit our website at https://www.my-aac.org | ||||||
|  |  | ||||||
|  | ## Project supported by JetBrains | ||||||
|  |  | ||||||
|  | Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects. | ||||||
|  |  | ||||||
|  | [](https://www.jetbrains.com/?from=https://github.com/slawkens) | ||||||
|  |  | ||||||
|  | ### License | ||||||
|  |  | ||||||
|  | This program and all associated files are released under the GNU Public License.   | ||||||
|  | See [LICENSE](https://github.com/slawkens/myaac/blob/master/LICENSE) for details. | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								SECURITY.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								SECURITY.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # Security Policy | ||||||
|  |  | ||||||
|  | ## Supported Versions | ||||||
|  |  | ||||||
|  | | Version | Supported          | | ||||||
|  | | ------- | ------------------ | | ||||||
|  | | 1.x.y   | :white_check_mark: | | ||||||
|  | | 0.9.x   | :x:                | | ||||||
|  | | 0.8.x   | :white_check_mark: | | ||||||
|  | | < 0.7   | :x:                | | ||||||
|  |  | ||||||
|  | ## Reporting a Vulnerability | ||||||
|  |  | ||||||
|  | If you found a security vulnerability, please write an email to security@my-aac.org | ||||||
|  |  | ||||||
|  | All reports will be taken very seriously, and a fix will be posted as soon as possible. | ||||||
| @@ -1 +1,2 @@ | |||||||
| <?php | <?php | ||||||
|  | // nothing yet here | ||||||
| @@ -2,6 +2,9 @@ | |||||||
| // few things we'll need | // few things we'll need | ||||||
| require '../common.php'; | require '../common.php'; | ||||||
|  |  | ||||||
|  | define('ADMIN_PANEL', true); | ||||||
|  | define('MYAAC_ADMIN', true); | ||||||
|  |  | ||||||
| if(file_exists(BASE . 'config.local.php')) { | if(file_exists(BASE . 'config.local.php')) { | ||||||
| 	require_once BASE . 'config.local.php'; | 	require_once BASE . 'config.local.php'; | ||||||
| } | } | ||||||
| @@ -12,8 +15,6 @@ if(file_exists(BASE . 'install') && (!isset($config['installed']) || !$config['i | |||||||
| 	throw new RuntimeException('Setup detected that <b>install/</b> directory exists. Please visit <a href="' . BASE_URL . 'install">this</a> url to start MyAAC Installation.<br/>Delete <b>install/</b> directory if you already installed MyAAC.<br/>Remember to REFRESH this page when you\'re done!'); | 	throw new RuntimeException('Setup detected that <b>install/</b> directory exists. Please visit <a href="' . BASE_URL . 'install">this</a> url to start MyAAC Installation.<br/>Delete <b>install/</b> directory if you already installed MyAAC.<br/>Remember to REFRESH this page when you\'re done!'); | ||||||
| } | } | ||||||
|  |  | ||||||
| define('ADMIN_PANEL', true); |  | ||||||
|  |  | ||||||
| $content = ''; | $content = ''; | ||||||
|  |  | ||||||
| // validate page | // validate page | ||||||
| @@ -27,6 +28,12 @@ define('PAGE', $page); | |||||||
| require SYSTEM . 'functions.php'; | require SYSTEM . 'functions.php'; | ||||||
| require SYSTEM . 'init.php'; | require SYSTEM . 'init.php'; | ||||||
|  |  | ||||||
|  | if(config('env') === 'dev') { | ||||||
|  | 	ini_set('display_errors', 1); | ||||||
|  | 	ini_set('display_startup_errors', 1); | ||||||
|  | 	error_reporting(E_ALL); | ||||||
|  | } | ||||||
|  |  | ||||||
| // event system | // event system | ||||||
| require_once SYSTEM . 'hooks.php'; | require_once SYSTEM . 'hooks.php'; | ||||||
| $hooks = new Hooks(); | $hooks = new Hooks(); | ||||||
| @@ -34,6 +41,7 @@ $hooks->load(); | |||||||
|  |  | ||||||
| require SYSTEM . 'status.php'; | require SYSTEM . 'status.php'; | ||||||
| require SYSTEM . 'login.php'; | require SYSTEM . 'login.php'; | ||||||
|  | require SYSTEM . 'migrate.php'; | ||||||
| require ADMIN . 'includes/functions.php'; | require ADMIN . 'includes/functions.php'; | ||||||
|  |  | ||||||
| $twig->addGlobal('config', $config); | $twig->addGlobal('config', $config); | ||||||
| @@ -45,7 +53,7 @@ if(!$logged || !admin()) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // include our page | // include our page | ||||||
| $file = SYSTEM . 'pages/admin/' . $page . '.php'; | $file = ADMIN . 'pages/' . $page . '.php'; | ||||||
| if(!@file_exists($file)) { | if(!@file_exists($file)) { | ||||||
| 	$page = '404'; | 	$page = '404'; | ||||||
| 	$file = SYSTEM . 'pages/404.php'; | 	$file = SYSTEM . 'pages/404.php'; | ||||||
| @@ -60,4 +68,4 @@ ob_end_clean(); | |||||||
| // template | // template | ||||||
| $template_path = 'template/'; | $template_path = 'template/'; | ||||||
| require ADMIN . $template_path . 'template.php'; | require ADMIN . $template_path . 'template.php'; | ||||||
| ?> |  | ||||||
|   | |||||||
| @@ -182,7 +182,7 @@ if ($id > 0) { | |||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			$lastDay = 0; | 			$lastDay = 0; | ||||||
| 			if($p_days != 0 && $p_days != PHP_INT_MAX ) { | 			if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) { | ||||||
| 				$lastDay = time(); | 				$lastDay = time(); | ||||||
| 			} else if ($lastDay != 0) { | 			} else if ($lastDay != 0) { | ||||||
| 				$lastDay = 0; | 				$lastDay = 0; | ||||||
| @@ -279,7 +279,13 @@ else if ($id > 0 && isset($account) && $account->isLoaded()) { | |||||||
| 						<?php | 						<?php | ||||||
| 						$acc_group = $account->getAccGroupId(); | 						$acc_group = $account->getAccGroupId(); | ||||||
| 						if ($hasTypeColumn) { | 						if ($hasTypeColumn) { | ||||||
| 							$acc_type = array("Normal", "Tutor", "Senior Tutor", "Gamemaster", "God"); ?>
 | 							$groups = new OTS_Groups_List(); | ||||||
|  | 
 | ||||||
|  | 							$acc_type = array("Normal", "Tutor", "Senior Tutor", "Gamemaster", "God"); | ||||||
|  | 							if ($groups->getHighestId() == 6) { | ||||||
|  | 								$acc_type = array("Normal", "Tutor", "Senior Tutor", "Gamemaster", "Community Manager", "God"); | ||||||
|  | 							} | ||||||
|  | 						?>
 | ||||||
| 							<div class="col-xs-6"> | 							<div class="col-xs-6"> | ||||||
| 								<label for="group" class="control-label">Account Type:</label> | 								<label for="group" class="control-label">Account Type:</label> | ||||||
| 								<select name="group" id="group" class="form-control"> | 								<select name="group" id="group" class="form-control"> | ||||||
| @@ -420,7 +426,7 @@ else if ($id > 0 && isset($account) && $account->isLoaded()) { | |||||||
| 		<div class="box-body"> | 		<div class="box-body"> | ||||||
| 			<form action="<?php echo $base; ?>" method="post"> | 			<form action="<?php echo $base; ?>" method="post"> | ||||||
| 				<div class="input-group input-group-sm"> | 				<div class="input-group input-group-sm"> | ||||||
| 					<input type="text" class="form-control" name="search_name" value="<?php echo $search_account; ?>" | 					<input type="text" class="form-control" name="search_name" value="<?php echo escapeHtml($search_account); ?>" | ||||||
| 						   maxlength="32" size="32"> | 						   maxlength="32" size="32"> | ||||||
| 					<span class="input-group-btn"> | 					<span class="input-group-btn"> | ||||||
|                           <button type="submit" type="button" class="btn btn-info btn-flat">Search</button> |                           <button type="submit" type="button" class="btn btn-info btn-flat">Search</button> | ||||||
| @@ -10,8 +10,8 @@ | |||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
| $title = 'Load items.xml'; | $title = 'Load items.xml'; | ||||||
| 
 | 
 | ||||||
| require LIBS . 'items.php'; | require_once LIBS . 'items.php'; | ||||||
| require LIBS . 'weapons.php'; | require_once LIBS . 'weapons.php'; | ||||||
| 
 | 
 | ||||||
| $twig->display('admin.items.html.twig'); | $twig->display('admin.items.html.twig'); | ||||||
| 
 | 
 | ||||||
| @@ -89,7 +89,7 @@ if (isset($_REQUEST['template'])) { | |||||||
| 		if (isset($menus[$id])) { | 		if (isset($menus[$id])) { | ||||||
| 			$i = 0; | 			$i = 0; | ||||||
| 			foreach ($menus[$id] as $menu) { | 			foreach ($menus[$id] as $menu) { | ||||||
| 				echo '<li class="ui-state-default" id="list-' . $id . '-' . $i . '"><label>Name:</label><input type="text" name="menu[' . $id . '][]" value="' . $menu['name'] . '"/>
 | 				echo '<li class="ui-state-default" id="list-' . $id . '-' . $i . '"><label>Name:</label><input type="text" name="menu[' . $id . '][]" value="' . escapeHtml($menu['name']) . '"/>
 | ||||||
| 				<label>Link:</label><input type="text" name="menu_link[' . $id . '][]" value="' . $menu['link'] . '"/> | 				<label>Link:</label><input type="text" name="menu_link[' . $id . '][]" value="' . $menu['link'] . '"/> | ||||||
| 				<input type="hidden" name="menu_blank[' . $id . '][]" value="0" /> | 				<input type="hidden" name="menu_blank[' . $id . '][]" value="0" /> | ||||||
| 				<label><input class="blank-checkbox" type="checkbox" ' . ($menu['blank'] == 1 ? 'checked' : '') . '/><span title="Open in New Window">Open in New Window</span></label> | 				<label><input class="blank-checkbox" type="checkbox" ' . ($menu['blank'] == 1 ? 'checked' : '') . '/><span title="Open in New Window">Open in New Window</span></label> | ||||||
| @@ -134,4 +134,4 @@ if (isset($_REQUEST['template'])) { | |||||||
| 	$twig->display('admin.menus.form.html.twig', array( | 	$twig->display('admin.menus.form.html.twig', array( | ||||||
| 		'templates' => $templates | 		'templates' => $templates | ||||||
| 	)); | 	)); | ||||||
| } | } | ||||||
| @@ -117,7 +117,7 @@ if($action == 'edit' || $action == 'new') { | |||||||
| 		'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'add'), | 		'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'add'), | ||||||
| 		'news_id' => isset($id) ? $id : null, | 		'news_id' => isset($id) ? $id : null, | ||||||
| 		'title' => isset($p_title) ? $p_title : '', | 		'title' => isset($p_title) ? $p_title : '', | ||||||
| 		'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '', | 		'body' => isset($body) ? escapeHtml($body) : '', | ||||||
| 		'type' => isset($type) ? $type : null, | 		'type' => isset($type) ? $type : null, | ||||||
| 		'player' => isset($player) && $player->isLoaded() ? $player : null, | 		'player' => isset($player) && $player->isLoaded() ? $player : null, | ||||||
| 		'player_id' => isset($player_id) ? $player_id : null, | 		'player_id' => isset($player_id) ? $player_id : null, | ||||||
| @@ -105,7 +105,7 @@ $twig->display('admin.pages.form.html.twig', array( | |||||||
| 	'title' => $p_title, | 	'title' => $p_title, | ||||||
| 	'php' => $php, | 	'php' => $php, | ||||||
| 	'enable_tinymce' => $enable_tinymce, | 	'enable_tinymce' => $enable_tinymce, | ||||||
| 	'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '', | 	'body' => isset($body) ? escapeHtml($body) : '', | ||||||
| 	'groups' => $groups->getGroups(), | 	'groups' => $groups->getGroups(), | ||||||
| 	'access' => $access | 	'access' => $access | ||||||
| )); | )); | ||||||
| @@ -196,5 +196,3 @@ class Pages | |||||||
| 		return !count($errors); | 		return !count($errors); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| ?>
 |  | ||||||
| @@ -650,7 +650,7 @@ else if ($id > 0 && isset($player) && $player->isLoaded()) | |||||||
| 											<label for="look_feet" class="control-label">Feet: <span | 											<label for="look_feet" class="control-label">Feet: <span | ||||||
| 														id="look_feet_val"></span></label> | 														id="look_feet_val"></span></label> | ||||||
| 											<input type="range" min="0" max="132" | 											<input type="range" min="0" max="132" | ||||||
| 												   value="<?php echo $player->getLookBody(); ?>" | 												   value="<?php echo $player->getLookFeet(); ?>" | ||||||
| 												   class="slider form-control" id="look_feet" name="look_feet"> | 												   class="slider form-control" id="look_feet" name="look_feet"> | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
| @@ -697,7 +697,14 @@ else if ($id > 0 && isset($player) && $player->isLoaded()) | |||||||
| 											<label for="lastip" class="control-label">Last IP:</label> | 											<label for="lastip" class="control-label">Last IP:</label> | ||||||
| 											<input type="text" class="form-control" id="lastip" name="lastip" | 											<input type="text" class="form-control" id="lastip" name="lastip" | ||||||
| 												   autocomplete="off" | 												   autocomplete="off" | ||||||
| 												   maxlength="10" value="<?php echo longToIp($player->getLastIP()); ?>" | 												   maxlength="10" value="<?php
 | ||||||
|  | 													if (strlen($player->getLastIP()) > 11) { | ||||||
|  | 														echo inet_ntop($player->getLastIP()); | ||||||
|  | 													} | ||||||
|  | 													else { | ||||||
|  | 														echo longToIp($player->getLastIP()); | ||||||
|  | 													} | ||||||
|  | 													?>"
 | ||||||
| 												   readonly/> | 												   readonly/> | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
| @@ -777,7 +784,7 @@ else if ($id > 0 && isset($player) && $player->isLoaded()) | |||||||
| 			<div class="box-body"> | 			<div class="box-body"> | ||||||
| 				<form action="<?php echo $base; ?>" method="post"> | 				<form action="<?php echo $base; ?>" method="post"> | ||||||
| 					<div class="input-group input-group-sm"> | 					<div class="input-group input-group-sm"> | ||||||
| 						<input type="text" class="form-control" name="search_name" value="<?php echo $search_name; ?>" | 						<input type="text" class="form-control" name="search_name" value="<?php echo escapeHtml($search_name); ?>" | ||||||
| 							   maxlength="32" size="32"> | 							   maxlength="32" size="32"> | ||||||
| 						<span class="input-group-btn"> | 						<span class="input-group-btn"> | ||||||
|                           <button type="submit" type="button" class="btn btn-info btn-flat">Search</button> |                           <button type="submit" type="button" class="btn btn-info btn-flat">Search</button> | ||||||
| @@ -885,15 +892,13 @@ else if ($id > 0 && isset($player) && $player->isLoaded()) | |||||||
|             var look_feet = $('#look_feet').val(); |             var look_feet = $('#look_feet').val(); | ||||||
|             var look_type = $('#look_type').val(); |             var look_type = $('#look_type').val(); | ||||||
| 
 | 
 | ||||||
|  |             var look_addons = ''; | ||||||
|             <?php if($hasLookAddons): ?>
 |             <?php if($hasLookAddons): ?>
 | ||||||
|                 var look_addons = '&addons=' + $('#look_addons').val(); |                 look_addons = '&addons=' + $('#look_addons').val(); | ||||||
| 	        <?php |  | ||||||
| 	        else: ?>
 |  | ||||||
| 	            var look_addons = ''; |  | ||||||
| 	        <?php endif; ?>
 | 	        <?php endif; ?>
 | ||||||
| 
 | 
 | ||||||
|             new_outfit = '<?= $config['outfit_images_url']; ?>?id=' + look_type + look_addons + '&head=' + look_head + '&body=' + look_body + '&legs=' + look_legs + '&feet=' + look_feet; |             new_outfit = '<?= $config['outfit_images_url']; ?>?id=' + look_type + look_addons + '&head=' + look_head + '&body=' + look_body + '&legs=' + look_legs + '&feet=' + look_feet; | ||||||
|             $("#player_outfit").attr("src", new_outfit); |             $("#player_outfit").attr("src", new_outfit); | ||||||
|             console.log(new_outfit); |             console.log(new_outfit); | ||||||
|         } |         } | ||||||
| 	</script> | 	</script> | ||||||
| @@ -36,4 +36,3 @@ $twig->display('admin.statistics.html.twig', array( | |||||||
| 	'account_type' => (USE_ACCOUNT_NAME ? 'name' : 'number'), | 	'account_type' => (USE_ACCOUNT_NAME ? 'name' : 'number'), | ||||||
| 	'points' => $points | 	'points' => $points | ||||||
| )); | )); | ||||||
| ?>
 |  | ||||||
| @@ -26,7 +26,7 @@ if ($version_compare == 0) { | |||||||
| 	success('MyAAC latest version is ' . $myaac_version . '. You\'re using the latest version. | 	success('MyAAC latest version is ' . $myaac_version . '. You\'re using the latest version. | ||||||
| 	<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here')); | 	<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here')); | ||||||
| } else if ($version_compare < 0) { | } else if ($version_compare < 0) { | ||||||
| 	echo success('Woah, seems you\'re using newer version as latest released one! MyAAC latest released version is ' . $myaac_version . ', and you\'re using version ' . MYAAC_VERSION . '. | 	success('Woah, seems you\'re using newer version as latest released one! MyAAC latest released version is ' . $myaac_version . ', and you\'re using version ' . MYAAC_VERSION . '. | ||||||
| 	<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here')); | 	<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here')); | ||||||
| } else { | } else { | ||||||
| 	warning('You\'re using outdated version.<br/> | 	warning('You\'re using outdated version.<br/> | ||||||
| @@ -47,4 +47,3 @@ function version_revert($version) | |||||||
| 	$release = $version; | 	$release = $version; | ||||||
| 	return $major . '.' . $minor . '.' . $release; | 	return $major . '.' . $minor . '.' . $release; | ||||||
| }*/ | }*/ | ||||||
| ?>
 |  | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| <?php | <?php | ||||||
|  | define('MYAAC_ADMIN', true); | ||||||
|  |  | ||||||
| require '../../common.php'; | require '../../common.php'; | ||||||
| require SYSTEM . 'functions.php'; | require SYSTEM . 'functions.php'; | ||||||
| require SYSTEM . 'init.php'; | require SYSTEM . 'init.php'; | ||||||
| @@ -11,4 +13,3 @@ if(!function_exists('phpinfo')) | |||||||
| 	die('phpinfo() disabled on this web server.'); | 	die('phpinfo() disabled on this web server.'); | ||||||
|  |  | ||||||
| phpinfo(); | phpinfo(); | ||||||
| ?> |  | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| <?php | <?php | ||||||
|  | define('MYAAC_ADMIN', true); | ||||||
|  |  | ||||||
| require '../../common.php'; | require '../../common.php'; | ||||||
| require SYSTEM . 'init.php'; | require SYSTEM . 'init.php'; | ||||||
| require SYSTEM . 'functions.php'; | require SYSTEM . 'functions.php'; | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								common.php
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								common.php
									
									
									
									
									
								
							| @@ -23,15 +23,15 @@ | |||||||
|  * @copyright 2019 MyAAC |  * @copyright 2019 MyAAC | ||||||
|  * @link      https://my-aac.org |  * @link      https://my-aac.org | ||||||
|  */ |  */ | ||||||
| if (version_compare(phpversion(), '5.5', '<')) die('PHP version 5.5 or higher is required.'); | if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.'); | ||||||
| session_start(); |  | ||||||
|  |  | ||||||
| define('MYAAC', true); | define('MYAAC', true); | ||||||
| define('MYAAC_VERSION', '0.8.2-dev'); | define('MYAAC_VERSION', '0.8.18'); | ||||||
| define('DATABASE_VERSION', 30); | define('DATABASE_VERSION', 33); | ||||||
| define('TABLE_PREFIX', 'myaac_'); | define('TABLE_PREFIX', 'myaac_'); | ||||||
| define('START_TIME', microtime(true)); | define('START_TIME', microtime(true)); | ||||||
| define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); | define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); | ||||||
|  | define('IS_CLI', in_array(php_sapi_name(), ['cli', 'phpdb'])); | ||||||
|  |  | ||||||
| // account flags | // account flags | ||||||
| define('FLAG_ADMIN', 1); | define('FLAG_ADMIN', 1); | ||||||
| @@ -85,6 +85,11 @@ define('TFS_03', 4); | |||||||
| define('TFS_FIRST', TFS_02); | define('TFS_FIRST', TFS_02); | ||||||
| define('TFS_LAST', TFS_03); | define('TFS_LAST', TFS_03); | ||||||
|  |  | ||||||
|  | if (!IS_CLI) { | ||||||
|  | 	session_save_path(SYSTEM . 'php_sessions'); | ||||||
|  | 	session_start(); | ||||||
|  | } | ||||||
|  |  | ||||||
| // basedir | // basedir | ||||||
| $basedir = ''; | $basedir = ''; | ||||||
| $tmp = explode('/', $_SERVER['SCRIPT_NAME']); | $tmp = explode('/', $_SERVER['SCRIPT_NAME']); | ||||||
| @@ -92,26 +97,31 @@ $size = count($tmp) - 1; | |||||||
| for($i = 1; $i < $size; $i++) | for($i = 1; $i < $size; $i++) | ||||||
| 	$basedir .= '/' . $tmp[$i]; | 	$basedir .= '/' . $tmp[$i]; | ||||||
|  |  | ||||||
| $basedir = str_replace(array('/admin', '/install'), '', $basedir); | $basedir = str_replace(array('/admin', '/install', '/tools'), '', $basedir); | ||||||
| define('BASE_DIR', $basedir); | define('BASE_DIR', $basedir); | ||||||
|  |  | ||||||
| if(isset($_SERVER['HTTP_HOST'][0])) { | if (file_exists(BASE . 'config.local.php') && !defined('MYAAC_INSTALL')) { | ||||||
| 	$baseHost = $_SERVER['HTTP_HOST']; | 	require BASE . 'config.local.php'; | ||||||
| } |  | ||||||
| else { |  | ||||||
| 	if(isset($_SERVER['SERVER_NAME'][0])) { |  | ||||||
| 		$baseHost = $_SERVER['SERVER_NAME']; |  | ||||||
| 	} |  | ||||||
| 	else { |  | ||||||
| 		$baseHost = $_SERVER['SERVER_ADDR']; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost); | if(!IS_CLI) { | ||||||
| define('BASE_URL', SERVER_URL . BASE_DIR . '/'); | 	if (isset($_SERVER['HTTP_HOST'][0])) { | ||||||
| define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/'); | 		$baseHost = $_SERVER['HTTP_HOST']; | ||||||
|  | 	} else { | ||||||
|  | 		if (isset($_SERVER['SERVER_NAME'][0])) { | ||||||
|  | 			$baseHost = $_SERVER['SERVER_NAME']; | ||||||
|  | 		} else { | ||||||
|  | 			$baseHost = $_SERVER['SERVER_ADDR']; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| //define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']); | 	define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost); | ||||||
|  | 	define('BASE_URL', SERVER_URL . BASE_DIR . '/'); | ||||||
|  | 	define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/'); | ||||||
|  |  | ||||||
| require SYSTEM . 'exception.php'; | 	//define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']); | ||||||
|  | 	if(@$config['env'] === 'dev') { | ||||||
|  | 		require SYSTEM . 'exception.php'; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| require SYSTEM . 'autoload.php'; | require SYSTEM . 'autoload.php'; | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								config.php
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								config.php
									
									
									
									
									
								
							| @@ -86,14 +86,21 @@ $config = array( | |||||||
| 	), | 	), | ||||||
|  |  | ||||||
| 	// images | 	// images | ||||||
| 	'outfit_images_url' => 'http://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit | 	'outfit_images_url' => 'https://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit | ||||||
| 	'item_images_url' => 'http://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder | 	'item_images_url' => 'https://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder | ||||||
|  |  | ||||||
| 	// account | 	// account | ||||||
| 	'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager) | 	'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager) | ||||||
| 	'account_create_auto_login' => false, // auto login after creating account? | 	'account_create_auto_login' => false, // auto login after creating account? | ||||||
| 	'account_create_character_create' => true, // allow directly to create character on create account page? | 	'account_create_character_create' => true, // allow directly to create character on create account page? | ||||||
| 	'account_mail_verify' => false, // force users to confirm their email addresses when registering account | 	'account_mail_verify' => false, // force users to confirm their email addresses when registering | ||||||
|  | 	'account_mail_confirmed_reward' => [ // reward users for confirming their E-Mails | ||||||
|  | 		// account_mail_verify needs to be enabled too | ||||||
|  | 		'premium_days' => 0, | ||||||
|  | 		'premium_points' => 0, | ||||||
|  | 		'coins' => 0, | ||||||
|  | 		'message' => 'You received %d %s for confirming your E-Mail address.' // example: You received 20 premium points for confirming your E-Mail address. | ||||||
|  | 	], | ||||||
| 	'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email) | 	'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email) | ||||||
| 	'account_premium_days' => 0, // default premium days on new account | 	'account_premium_days' => 0, // default premium days on new account | ||||||
| 	'account_premium_points' => 0, // default premium points on new account | 	'account_premium_points' => 0, // default premium points on new account | ||||||
| @@ -151,12 +158,17 @@ $config = array( | |||||||
| 		4 => 'Knight Sample' | 		4 => 'Knight Sample' | ||||||
| 	), | 	), | ||||||
|  |  | ||||||
|  | 	'use_character_sample_skills' => false, | ||||||
|  |  | ||||||
|  | 	// it must show limited number of players after using search in character page | ||||||
|  | 	'characters_search_limit' => 15, | ||||||
|  |  | ||||||
| 	// town list used when creating character | 	// town list used when creating character | ||||||
| 	// won't be displayed if there is only one item (rookgaard for example) | 	// won't be displayed if there is only one item (rookgaard for example) | ||||||
| 	'character_towns' => array(1), | 	'character_towns' => array(1), | ||||||
|  |  | ||||||
| 	// characters lenght | 	// characters length | ||||||
| 	// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum lenght be 21. | 	// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum length to be 21. | ||||||
| 	'character_name_min_length' => 4, | 	'character_name_min_length' => 4, | ||||||
| 	'character_name_max_length' => 21, | 	'character_name_max_length' => 21, | ||||||
|  |  | ||||||
| @@ -221,7 +233,10 @@ $config = array( | |||||||
| 		'frags' => false, | 		'frags' => false, | ||||||
| 		'deleted' => false, // should deleted characters from same account be still listed on the list of characters? When enabled it will show that character is "[DELETED]" | 		'deleted' => false, // should deleted characters from same account be still listed on the list of characters? When enabled it will show that character is "[DELETED]" | ||||||
| 	), | 	), | ||||||
| 	'quests' => array(), // quests list (displayed in character view), name => storage | 	'quests' => array( | ||||||
|  | 		//'Some Quest' => 123, | ||||||
|  | 		//'Some Quest Two' => 456, | ||||||
|  | 	), // quests list (displayed in character view), name => storage | ||||||
| 	'signature_enabled' => true, | 	'signature_enabled' => true, | ||||||
| 	'signature_type' => 'tibian', // signature engine to use: tibian, mango, gesior | 	'signature_type' => 'tibian', // signature engine to use: tibian, mango, gesior | ||||||
| 	'signature_cache_time' => 5, // how long to store cached file (in minutes), default 5 minutes | 	'signature_cache_time' => 5, // how long to store cached file (in minutes), default 5 minutes | ||||||
| @@ -252,9 +267,10 @@ $config = array( | |||||||
| 	'last_kills_limit' => 50, // max. number of deaths shown on the last kills page | 	'last_kills_limit' => 50, // max. number of deaths shown on the last kills page | ||||||
|  |  | ||||||
| 	// status, took automatically from config file if empty | 	// status, took automatically from config file if empty | ||||||
| 	'status_ip' => '', | 	'status_enabled' => true, // you can disable status checking by settings this to "false" | ||||||
|  | 	'status_ip' => '127.0.0.1', | ||||||
| 	'status_port' => '', | 	'status_port' => '', | ||||||
| 	'status_timeout' => 2, // how long to wait for the initial response from the server (default: 2 seconds) | 	'status_timeout' => 1.0, // how long to wait for the initial response from the server (default: 1 second) | ||||||
|  |  | ||||||
| 	// how often to connect to server and update status (default: every minute) | 	// how often to connect to server and update status (default: every minute) | ||||||
| 	// if your status timeout in config.lua is bigger, that it will be used instead | 	// if your status timeout in config.lua is bigger, that it will be used instead | ||||||
| @@ -273,5 +289,13 @@ $config = array( | |||||||
| 	'date_timezone' => 'Europe/Berlin', // more info at http://php.net/manual/en/timezones.php | 	'date_timezone' => 'Europe/Berlin', // more info at http://php.net/manual/en/timezones.php | ||||||
| 	'footer_show_load_time' => true, // display load time of the page in the footer | 	'footer_show_load_time' => true, // display load time of the page in the footer | ||||||
|  |  | ||||||
| 	'npc' => array() | 	'npc' => array(), | ||||||
|  |  | ||||||
|  | 	// character name blocked | ||||||
|  | 	'character_name_blocked' => array( | ||||||
|  | 		'prefix' => array(), | ||||||
|  | 		'names' => array(), | ||||||
|  | 		'words' => array(), | ||||||
|  | 	), | ||||||
|  |  | ||||||
| ); | ); | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								images/gallery/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								images/gallery/index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										84
									
								
								index.php
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								index.php
									
									
									
									
									
								
							| @@ -38,7 +38,7 @@ else | |||||||
| $uri = str_replace(array('index.php/', '?'), '', $uri); | $uri = str_replace(array('index.php/', '?'), '', $uri); | ||||||
| define('URI', $uri); | define('URI', $uri); | ||||||
|  |  | ||||||
| if(preg_match("/^[A-Za-z0-9-_%\'+]+\.png$/i", $uri)) { | if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) { | ||||||
| 	$tmp = explode('.', $uri); | 	$tmp = explode('.', $uri); | ||||||
| 	$_REQUEST['name'] = urldecode($tmp[0]); | 	$_REQUEST['name'] = urldecode($tmp[0]); | ||||||
|  |  | ||||||
| @@ -48,7 +48,7 @@ if(preg_match("/^[A-Za-z0-9-_%\'+]+\.png$/i", $uri)) { | |||||||
| } | } | ||||||
|  |  | ||||||
| if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|php|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) { | if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|php|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) { | ||||||
| 	header('HTTP/1.0 404 Not Found'); | 	http_response_code(404); | ||||||
| 	exit; | 	exit; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -56,11 +56,17 @@ if(file_exists(BASE . 'config.local.php')) { | |||||||
| 	require_once BASE . 'config.local.php'; | 	require_once BASE . 'config.local.php'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | ini_set('log_errors', 1); | ||||||
| if(config('env') === 'dev') { | if(config('env') === 'dev') { | ||||||
| 	ini_set('display_errors', 1); | 	ini_set('display_errors', 1); | ||||||
| 	ini_set('display_startup_errors', 1); | 	ini_set('display_startup_errors', 1); | ||||||
| 	error_reporting(E_ALL); | 	error_reporting(E_ALL); | ||||||
| } | } | ||||||
|  | else { | ||||||
|  | 	ini_set('display_errors', 0); | ||||||
|  | 	ini_set('display_startup_errors', 0); | ||||||
|  | 	error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT); | ||||||
|  | } | ||||||
|  |  | ||||||
| if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . 'install')) | if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . 'install')) | ||||||
| { | { | ||||||
| @@ -68,6 +74,14 @@ if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . | |||||||
| 	throw new RuntimeException('Setup detected that <b>install/</b> directory exists. Please visit <a href="' . BASE_URL . 'install">this</a> url to start MyAAC Installation.<br/>Delete <b>install/</b> directory if you already installed MyAAC.<br/>Remember to REFRESH this page when you\'re done!'); | 	throw new RuntimeException('Setup detected that <b>install/</b> directory exists. Please visit <a href="' . BASE_URL . 'install">this</a> url to start MyAAC Installation.<br/>Delete <b>install/</b> directory if you already installed MyAAC.<br/>Remember to REFRESH this page when you\'re done!'); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | require_once SYSTEM . 'init.php'; | ||||||
|  | require_once SYSTEM . 'template.php'; | ||||||
|  |  | ||||||
|  | // verify myaac tables exists in database | ||||||
|  | if(!$db->hasTable('myaac_account_actions')) { | ||||||
|  | 	throw new RuntimeException('Seems that the table <strong>myaac_account_actions</strong> of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting <a href="' . BASE_URL . 'install">this</a> url.'); | ||||||
|  | } | ||||||
|  |  | ||||||
| $found = false; | $found = false; | ||||||
| if(empty($uri) || isset($_REQUEST['template'])) { | if(empty($uri) || isset($_REQUEST['template'])) { | ||||||
| 	$_REQUEST['p'] = 'news'; | 	$_REQUEST['p'] = 'news'; | ||||||
| @@ -75,7 +89,11 @@ if(empty($uri) || isset($_REQUEST['template'])) { | |||||||
| } | } | ||||||
| else { | else { | ||||||
| 	$tmp = strtolower($uri); | 	$tmp = strtolower($uri); | ||||||
| 	if(!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(SYSTEM . 'pages/' . $tmp . '.php')) { | 	if (!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(TEMPLATES . $template_name . '/pages/' . $tmp . '.php')) { | ||||||
|  | 		$_REQUEST['p'] = $uri; | ||||||
|  | 		$found = true; | ||||||
|  | 	} | ||||||
|  | 	else if (!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(SYSTEM . 'pages/' . $tmp . '.php')) { | ||||||
| 		$_REQUEST['p'] = $uri; | 		$_REQUEST['p'] = $uri; | ||||||
| 		$found = true; | 		$found = true; | ||||||
| 	} | 	} | ||||||
| @@ -127,13 +145,13 @@ else { | |||||||
| 			'/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view') | 			'/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view') | ||||||
| 		); | 		); | ||||||
|  |  | ||||||
| 		foreach($rules as $rule => $redirect) { | 		foreach ($rules as $rule => $redirect) { | ||||||
| 			if (preg_match($rule, $uri)) { | 			if (preg_match($rule, $uri)) { | ||||||
| 				$tmp = explode('/', $uri); | 				$tmp = explode('/', $uri); | ||||||
| 				/* @var $redirect array */ | 				/* @var $redirect array */ | ||||||
| 				foreach($redirect as $key => $value) { | 				foreach ($redirect as $key => $value) { | ||||||
|  |  | ||||||
| 					if(strpos($value, '$') !== false) { | 					if (strpos($value, '$') !== false) { | ||||||
| 						$value = str_replace('$' . $value[1], $tmp[$value[1]], $value); | 						$value = str_replace('$' . $value[1], $tmp[$value[1]], $value); | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| @@ -148,6 +166,12 @@ else { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // handle ?fbclid=x, etc. (show news page) | ||||||
|  | if (!$found && count($_GET) > 0 && !isset($_REQUEST['subtopic']) && !isset($_REQUEST['p']) && !in_array($_SERVER['QUERY_STRING'], getDatabasePages())) { | ||||||
|  | 	$_REQUEST['p'] = $_REQUEST['subtopic'] = 'news'; | ||||||
|  | 	$found = true; | ||||||
|  | } | ||||||
|  |  | ||||||
| // define page visited, so it can be used within events system | // define page visited, so it can be used within events system | ||||||
| $page = isset($_REQUEST['subtopic']) ? $_REQUEST['subtopic'] : (isset($_REQUEST['p']) ? $_REQUEST['p'] : ''); | $page = isset($_REQUEST['subtopic']) ? $_REQUEST['subtopic'] : (isset($_REQUEST['p']) ? $_REQUEST['p'] : ''); | ||||||
| if(empty($page) || !preg_match('/^[A-z0-9\_\-]+$/', $page)) { | if(empty($page) || !preg_match('/^[A-z0-9\_\-]+$/', $page)) { | ||||||
| @@ -168,44 +192,17 @@ define('PAGE', $page); | |||||||
|  |  | ||||||
| $template_place_holders = array(); | $template_place_holders = array(); | ||||||
|  |  | ||||||
| require_once SYSTEM . 'init.php'; |  | ||||||
|  |  | ||||||
| // event system | // event system | ||||||
| require_once SYSTEM . 'hooks.php'; | require_once SYSTEM . 'hooks.php'; | ||||||
| $hooks = new Hooks(); | $hooks = new Hooks(); | ||||||
| $hooks->load(); | $hooks->load(); | ||||||
| require_once SYSTEM . 'template.php'; |  | ||||||
| require_once SYSTEM . 'login.php'; | require_once SYSTEM . 'login.php'; | ||||||
| require_once SYSTEM . 'status.php'; | require_once SYSTEM . 'status.php'; | ||||||
|  |  | ||||||
| $twig->addGlobal('config', $config); | $twig->addGlobal('config', $config); | ||||||
| $twig->addGlobal('status', $status); | $twig->addGlobal('status', $status); | ||||||
|  |  | ||||||
| // verify myaac tables exists in database | require SYSTEM . 'migrate.php'; | ||||||
| if(!$db->hasTable('myaac_account_actions')) { |  | ||||||
| 	throw new RuntimeException('Seems that the table <strong>myaac_account_actions</strong> of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting <a href="' . BASE_URL . 'install">this</a> url.'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // database migrations |  | ||||||
| $tmp = ''; |  | ||||||
| if(fetchDatabaseConfig('database_version', $tmp)) { // we got version |  | ||||||
| 	$tmp = (int)$tmp; |  | ||||||
| 	if($tmp < DATABASE_VERSION) { // import if older |  | ||||||
| 		$db->revalidateCache(); |  | ||||||
| 		for($i = $tmp + 1; $i <= DATABASE_VERSION; $i++) { |  | ||||||
| 			require SYSTEM . 'migrations/' . $i . '.php'; |  | ||||||
| 			updateDatabaseConfig('database_version', $i); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| else { // register first version |  | ||||||
| 	registerDatabaseConfig('database_version', 0); |  | ||||||
| 	$db->revalidateCache(); |  | ||||||
| 	for($i = 1; $i <= DATABASE_VERSION; $i++) { |  | ||||||
| 		require SYSTEM . 'migrations/' . $i . '.php'; |  | ||||||
| 		updateDatabaseConfig('database_version', $i); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| $hooks->trigger(HOOK_STARTUP); | $hooks->trigger(HOOK_STARTUP); | ||||||
|  |  | ||||||
| @@ -301,6 +298,7 @@ if($config['backward_support']) { | |||||||
| 	$config['site'] = &$config; | 	$config['site'] = &$config; | ||||||
| 	$config['server'] = &$config['lua']; | 	$config['server'] = &$config['lua']; | ||||||
| 	$config['site']['shop_system'] = $config['gifts_system']; | 	$config['site']['shop_system'] = $config['gifts_system']; | ||||||
|  | 	$config['site']['gallery_page'] = true; | ||||||
|  |  | ||||||
| 	if(!isset($config['vdarkborder'])) | 	if(!isset($config['vdarkborder'])) | ||||||
| 		$config['vdarkborder'] = '#505050'; | 		$config['vdarkborder'] = '#505050'; | ||||||
| @@ -325,8 +323,10 @@ if($load_it) | |||||||
| 	if(SITE_CLOSED && admin()) | 	if(SITE_CLOSED && admin()) | ||||||
| 		$content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>'; | 		$content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>'; | ||||||
|  |  | ||||||
| 	if($config['backward_support']) | 	if($config['backward_support']) { | ||||||
| 		require SYSTEM . 'compat_pages.php'; | 		require SYSTEM . 'compat/pages.php'; | ||||||
|  | 		require SYSTEM . 'compat/classes.php'; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	$ignore = false; | 	$ignore = false; | ||||||
|  |  | ||||||
| @@ -346,11 +346,13 @@ if($load_it) | |||||||
| 				)) . $content; | 				)) . $content; | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		$file = SYSTEM . 'pages/' . $page . '.php'; | 		$file = TEMPLATES . "$template_name/pages/$page.php"; | ||||||
| 		if(!@file_exists($file)) | 		if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) { | ||||||
| 		{ | 			$file = SYSTEM . "pages/$page.php"; | ||||||
| 			$page = '404'; | 			if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) { | ||||||
| 			$file = SYSTEM . 'pages/404.php'; | 				$page = '404'; | ||||||
|  | 				$file = SYSTEM . 'pages/404.php'; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,4 +38,3 @@ if(!isset($error) || !$error) { | |||||||
| 		$error = true; | 		$error = true; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| ?> |  | ||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | SET @myaac_database_version = 33; | ||||||
|  |  | ||||||
| CREATE TABLE `myaac_account_actions` | CREATE TABLE `myaac_account_actions` | ||||||
| ( | ( | ||||||
| 	`account_id` INT(11) NOT NULL, | 	`account_id` INT(11) NOT NULL, | ||||||
| @@ -57,6 +59,8 @@ CREATE TABLE `myaac_config` | |||||||
| 	UNIQUE (`name`) | 	UNIQUE (`name`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; | ||||||
|  |  | ||||||
|  | INSERT INTO `myaac_config` (`name`, `value`) VALUES ('database_version', @myaac_database_version); | ||||||
|  |  | ||||||
| CREATE TABLE `myaac_faq` | CREATE TABLE `myaac_faq` | ||||||
| ( | ( | ||||||
| 	`id` INT(11) NOT NULL AUTO_INCREMENT, | 	`id` INT(11) NOT NULL AUTO_INCREMENT, | ||||||
| @@ -320,9 +324,9 @@ CREATE TABLE `myaac_spells` | |||||||
|  |  | ||||||
| CREATE TABLE `myaac_visitors` | CREATE TABLE `myaac_visitors` | ||||||
| ( | ( | ||||||
| 	`ip` VARCHAR(16) NOT NULL, | 	`ip` VARCHAR(45) NOT NULL, | ||||||
| 	`lastvisit` INT(11) NOT NULL DEFAULT 0, | 	`lastvisit` INT(11) NOT NULL DEFAULT 0, | ||||||
| 	`page` VARCHAR(100) NOT NULL, | 	`page` VARCHAR(2048) NOT NULL, | ||||||
| 	UNIQUE (`ip`) | 	UNIQUE (`ip`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| We have detected that you don't have access to write to the system/cache directory. Under linux you can fix it by using this two command, where first one should be enough (for apache):<br/><br/><span class="console">chown -R www-data.www-data /var/www/*</span><br/><span class="console">chmod -R 660 system/cache</span> | We have detected that you don't have access to write to the system/cache directory. Under linux you can fix it by using this two command, where first one should be enough (for apache):<br/><br/><span class="console">chown -R www-data.www-data /var/www/*</span><br/><span class="console">chmod -R 760 system/cache</span> | ||||||
|  |  | ||||||
| <style type="text/css"> | <style type="text/css"> | ||||||
| .console { | .console { | ||||||
| 	font-family:Courier; | 	font-family: Courier,serif; | ||||||
| 	color: #CCCCCC; | 	color: #CCCCCC; | ||||||
| 	background: #000000; | 	background: #000000; | ||||||
| 	border: 3px double #CCCCCC; | 	border: 3px double #CCCCCC; | ||||||
| 	padding: 0px; | 	padding: 0; | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
| @@ -70,7 +70,7 @@ if($step == 'database') { | |||||||
|  |  | ||||||
| 		$key = str_replace('var_', '', $key); | 		$key = str_replace('var_', '', $key); | ||||||
|  |  | ||||||
| 		if(in_array($key, array('account', 'password', 'email', 'player_name'))) { | 		if(in_array($key, array('account', 'account_id', 'password', 'email', 'player_name'))) { | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -114,14 +114,12 @@ if($step == 'database') { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| else if($step == 'admin') { | else if($step == 'admin') { | ||||||
| 	$config_failed = true; | 	if(!file_exists(BASE . 'config.local.php') || !isset($config['installed']) || !$config['installed']) { | ||||||
| 	if(file_exists(BASE . 'config.local.php') && isset($config['installed']) && $config['installed'] && isset($_SESSION['saved'])) { |  | ||||||
| 		$config_failed = false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if($config_failed) { |  | ||||||
| 		$step = 'database'; | 		$step = 'database'; | ||||||
| 	} | 	} | ||||||
|  | 	else { | ||||||
|  | 		$_SESSION['saved'] = true; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| else if($step == 'finish') { | else if($step == 'finish') { | ||||||
| 	$email = $_SESSION['var_email']; | 	$email = $_SESSION['var_email']; | ||||||
|   | |||||||
| @@ -5,4 +5,3 @@ $twig->display('install.license.html.twig', array( | |||||||
| 	'license' => file_get_contents(BASE . 'LICENSE'), | 	'license' => file_get_contents(BASE . 'LICENSE'), | ||||||
| 	'buttons' => next_buttons() | 	'buttons' => next_buttons() | ||||||
| )); | )); | ||||||
| ?> |  | ||||||
|   | |||||||
| @@ -1,6 +1,10 @@ | |||||||
| <?php | <?php | ||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
|  | // configuration | ||||||
|  | $extensions_required = [ | ||||||
|  | 	'pdo', 'pdo_mysql', 'xml', 'zip' | ||||||
|  | ]; | ||||||
| /* | /* | ||||||
|  * |  * | ||||||
|  * @param string $name |  * @param string $name | ||||||
| @@ -35,9 +39,11 @@ version_check('register_long_arrays', !$ini_register_globals, $ini_register_glob | |||||||
| $ini_safe_mode = ini_get_bool('safe_mode'); | $ini_safe_mode = ini_get_bool('safe_mode'); | ||||||
| version_check('safe_mode', !$ini_safe_mode, $ini_safe_mode ? $locale['on'] : $locale['off'], true); | version_check('safe_mode', !$ini_safe_mode, $ini_safe_mode ? $locale['on'] : $locale['off'], true); | ||||||
|  |  | ||||||
| version_check(str_replace('$EXTENSION$', 'PDO', $locale['step_requirements_extension']) , extension_loaded('pdo'), extension_loaded('pdo') ? $locale['loaded'] : $locale['not_loaded']); | foreach ($extensions_required as $ext) { | ||||||
| version_check(str_replace('$EXTENSION$', 'XML', $locale['step_requirements_extension']), extension_loaded('xml'), extension_loaded('xml') ? $locale['loaded'] : $locale['not_loaded']); | 	$loaded = extension_loaded($ext); | ||||||
| version_check(str_replace('$EXTENSION$', 'ZIP', $locale['step_requirements_extension']), extension_loaded('zip'), extension_loaded('zip') ? $locale['loaded'] : $locale['not_loaded']); | 	version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded']); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| if($failed) | if($failed) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -18,4 +18,3 @@ $twig->display('install.config.html.twig', array( | |||||||
| 	'errors' => isset($errors) ? $errors : null, | 	'errors' => isset($errors) ? $errors : null, | ||||||
| 	'buttons' => next_buttons() | 	'buttons' => next_buttons() | ||||||
| )); | )); | ||||||
| ?> |  | ||||||
| @@ -57,16 +57,35 @@ if(!$error) { | |||||||
| 			error($database_error); | 			error($database_error); | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			$twig->display('install.installer.html.twig', array( | 			if(!$db->hasTable('accounts')) { | ||||||
| 				'url' => 'tools/5-database.php', | 				$tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']); | ||||||
| 				'message' => $locale['loading_spinner'] | 				error($tmp); | ||||||
| 			)); | 				$error = true; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if(!$db->hasTable('players')) { | ||||||
|  | 				$tmp = str_replace('$TABLE$', 'players', $locale['step_database_error_table']); | ||||||
|  | 				error($tmp); | ||||||
|  | 				$error = true; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if(!$db->hasTable('guilds')) { | ||||||
|  | 				$tmp = str_replace('$TABLE$', 'guilds', $locale['step_database_error_table']); | ||||||
|  | 				error($tmp); | ||||||
|  | 				$error = true; | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			if(!$error) { | 			if(!$error) { | ||||||
|  | 				$twig->display('install.installer.html.twig', array( | ||||||
|  | 					'url' => 'tools/5-database.php', | ||||||
|  | 					'message' => $locale['loading_spinner'] | ||||||
|  | 				)); | ||||||
|  |  | ||||||
| 				if(!Validator::email($_SESSION['var_mail_admin'])) { | 				if(!Validator::email($_SESSION['var_mail_admin'])) { | ||||||
| 					error($locale['step_config_mail_admin_error']); | 					error($locale['step_config_mail_admin_error']); | ||||||
| 					$error = true; | 					$error = true; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if(!Validator::email($_SESSION['var_mail_address'])) { | 				if(!Validator::email($_SESSION['var_mail_address'])) { | ||||||
| 					error($locale['step_config_mail_address_error']); | 					error($locale['step_config_mail_address_error']); | ||||||
| 					$error = true; | 					$error = true; | ||||||
| @@ -82,6 +101,7 @@ if(!$error) { | |||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if($saved) { | 				if($saved) { | ||||||
|  | 					success($locale['step_database_config_saved']); | ||||||
| 					if(!$error) { | 					if(!$error) { | ||||||
| 						$_SESSION['saved'] = true; | 						$_SESSION['saved'] = true; | ||||||
| 					} | 					} | ||||||
| @@ -91,7 +111,7 @@ if(!$error) { | |||||||
| 					unset($_SESSION['saved']); | 					unset($_SESSION['saved']); | ||||||
|  |  | ||||||
| 					$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']); | 					$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']); | ||||||
| 					warning($locale['step_database_error_file'] . '<br/> | 					error($locale['step_database_error_file'] . '<br/> | ||||||
| 						<textarea cols="70" rows="10">' . $content . '</textarea>'); | 						<textarea cols="70" rows="10">' . $content . '</textarea>'); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -102,6 +122,6 @@ if(!$error) { | |||||||
|  |  | ||||||
| <form action="<?php echo BASE_URL; ?>install/" method="post"> | <form action="<?php echo BASE_URL; ?>install/" method="post"> | ||||||
| 	<input type="hidden" name="step" id="step" value="admin" /> | 	<input type="hidden" name="step" id="step" value="admin" /> | ||||||
| 	<?php echo next_buttons(true, $error ? false : true); | 	<?php echo next_buttons(true, !$error); | ||||||
| 	?> | 	?> | ||||||
| </form> | </form> | ||||||
|   | |||||||
| @@ -66,7 +66,6 @@ else { | |||||||
| 			$new_account->setPassword(encrypt($password)); | 			$new_account->setPassword(encrypt($password)); | ||||||
| 			$new_account->setEMail($email); | 			$new_account->setEMail($email); | ||||||
|  |  | ||||||
| 			$new_account->unblock(); |  | ||||||
| 			$new_account->save(); | 			$new_account->save(); | ||||||
|  |  | ||||||
| 			$new_account->setCustomField('created', time()); | 			$new_account->setCustomField('created', time()); | ||||||
| @@ -83,7 +82,7 @@ else { | |||||||
| 		if($db->hasColumn('accounts', 'group_id')) | 		if($db->hasColumn('accounts', 'group_id')) | ||||||
| 			$account_used->setCustomField('group_id', $groups->getHighestId()); | 			$account_used->setCustomField('group_id', $groups->getHighestId()); | ||||||
| 		if($db->hasColumn('accounts', 'type')) | 		if($db->hasColumn('accounts', 'type')) | ||||||
| 			$account_used->setCustomField('type', 5); | 			$account_used->setCustomField('type', 6); | ||||||
|  |  | ||||||
| 		if(!$player_db->isLoaded()) | 		if(!$player_db->isLoaded()) | ||||||
| 			$player->setAccountId($account_used->getId()); | 			$player->setAccountId($account_used->getId()); | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| <?php | <?php | ||||||
|  | define('MYAAC_INSTALL', true); | ||||||
|  |  | ||||||
| require_once '../../common.php'; | require_once '../../common.php'; | ||||||
|  |  | ||||||
| require SYSTEM . 'functions.php'; | require SYSTEM . 'functions.php'; | ||||||
| @@ -9,8 +11,10 @@ $error = false; | |||||||
| require BASE . 'install/includes/config.php'; | require BASE . 'install/includes/config.php'; | ||||||
|  |  | ||||||
| ini_set('max_execution_time', 300); | ini_set('max_execution_time', 300); | ||||||
|  |  | ||||||
|  | @ob_end_flush(); | ||||||
| ob_implicit_flush(); | ob_implicit_flush(); | ||||||
| ob_end_flush(); |  | ||||||
| header('X-Accel-Buffering: no'); | header('X-Accel-Buffering: no'); | ||||||
|  |  | ||||||
| if(!$error) { | if(!$error) { | ||||||
| @@ -21,24 +25,6 @@ if(!$error) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| if(!$db->hasTable('accounts')) { |  | ||||||
| 	$locale['step_database_error_table'] = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']); |  | ||||||
| 	error($locale['step_database_error_table']); |  | ||||||
| 	return; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if(!$db->hasTable('players')) { |  | ||||||
| 	$locale['step_database_error_table'] = str_replace('$TABLE$', 'players', $locale['step_database_error_table']); |  | ||||||
| 	error($locale['step_database_error_table']); |  | ||||||
| 	return; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if(!$db->hasTable('guilds')) { |  | ||||||
| 	$locale['step_database_error_table'] = str_replace('$TABLE$', 'guilds', $locale['step_database_error_table']); |  | ||||||
| 	error($locale['step_database_error_table']); |  | ||||||
| 	return; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if($db->hasTable(TABLE_PREFIX . 'account_actions')) { | if($db->hasTable(TABLE_PREFIX . 'account_actions')) { | ||||||
| 	$locale['step_database_error_table_exist'] = str_replace('$TABLE$', TABLE_PREFIX . 'account_actions', $locale['step_database_error_table_exist']); | 	$locale['step_database_error_table_exist'] = str_replace('$TABLE$', TABLE_PREFIX . 'account_actions', $locale['step_database_error_table_exist']); | ||||||
| 	warning($locale['step_database_error_table_exist']); | 	warning($locale['step_database_error_table_exist']); | ||||||
| @@ -48,7 +34,6 @@ else { | |||||||
| 	try { | 	try { | ||||||
| 		$db->query(file_get_contents(BASE . 'install/includes/schema.sql')); | 		$db->query(file_get_contents(BASE . 'install/includes/schema.sql')); | ||||||
|  |  | ||||||
| 		registerDatabaseConfig('database_version', DATABASE_VERSION); |  | ||||||
| 		$locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']); | 		$locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']); | ||||||
| 		success($locale['step_database_success_schema']); | 		success($locale['step_database_success_schema']); | ||||||
| 	} | 	} | ||||||
| @@ -72,13 +57,8 @@ else { | |||||||
| 		success($locale['step_database_adding_field'] . ' accounts.key...'); | 		success($locale['step_database_adding_field'] . ' accounts.key...'); | ||||||
| } | } | ||||||
|  |  | ||||||
| if(!$db->hasColumn('accounts', 'blocked')) { |  | ||||||
| 	if(query("ALTER TABLE `accounts` ADD `blocked` TINYINT(1) NOT NULL DEFAULT FALSE COMMENT 'internal usage' AFTER `key`;")) |  | ||||||
| 		success($locale['step_database_adding_field'] . ' accounts.blocked...'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if(!$db->hasColumn('accounts', 'created')) { | if(!$db->hasColumn('accounts', 'created')) { | ||||||
| 	if(query("ALTER TABLE `accounts` ADD `created` INT(11) NOT NULL DEFAULT 0 AFTER `" . ($db->hasColumn('accounts', 'group_id') ? 'group_id' : 'blocked') . "`;")) | 	if(query("ALTER TABLE `accounts` ADD `created` INT(11) NOT NULL DEFAULT 0 AFTER `" . ($db->hasColumn('accounts', 'group_id') ? 'group_id' : 'email') . "`;")) | ||||||
| 		success($locale['step_database_adding_field'] . ' accounts.created...'); | 		success($locale['step_database_adding_field'] . ' accounts.created...'); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -247,4 +227,4 @@ if($db->hasTable('z_forum')) { | |||||||
| 			success($locale['step_database_adding_field'] . ' z_forum.closed...'); | 			success($locale['step_database_adding_field'] . ' z_forum.closed...'); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| <?php | <?php | ||||||
|  | define('MYAAC_INSTALL', true); | ||||||
|  |  | ||||||
| require_once '../../common.php'; | require_once '../../common.php'; | ||||||
|  |  | ||||||
| require SYSTEM . 'functions.php'; | require SYSTEM . 'functions.php'; | ||||||
| @@ -6,8 +8,10 @@ require BASE . 'install/includes/functions.php'; | |||||||
| require BASE . 'install/includes/locale.php'; | require BASE . 'install/includes/locale.php'; | ||||||
|  |  | ||||||
| ini_set('max_execution_time', 300); | ini_set('max_execution_time', 300); | ||||||
|  |  | ||||||
|  | @ob_end_flush(); | ||||||
| ob_implicit_flush(); | ob_implicit_flush(); | ||||||
| ob_end_flush(); |  | ||||||
| header('X-Accel-Buffering: no'); | header('X-Accel-Buffering: no'); | ||||||
|  |  | ||||||
| if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) { | if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) { | ||||||
| @@ -34,10 +38,10 @@ function insert_sample_if_not_exist($p) { | |||||||
|  |  | ||||||
| $success = true; | $success = true; | ||||||
| insert_sample_if_not_exist(array('name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400)); | insert_sample_if_not_exist(array('name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400)); | ||||||
| insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470)); | insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470)); | ||||||
| insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470)); | insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470)); | ||||||
| insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470)); | insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470)); | ||||||
| insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470)); | insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470)); | ||||||
|  |  | ||||||
| if($success) { | if($success) { | ||||||
| 	success($locale['step_database_imported_players']); | 	success($locale['step_database_imported_players']); | ||||||
| @@ -91,6 +95,7 @@ require_once SYSTEM . 'migrations/22.php'; | |||||||
|  |  | ||||||
| // add myaac_pages pages | // add myaac_pages pages | ||||||
| require_once SYSTEM . 'migrations/27.php'; | require_once SYSTEM . 'migrations/27.php'; | ||||||
|  | require_once SYSTEM . 'migrations/30.php'; | ||||||
|  |  | ||||||
| $locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']); | $locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']); | ||||||
| $locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']); | $locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']); | ||||||
|   | |||||||
							
								
								
									
										285
									
								
								login.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								login.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | |||||||
|  | <?php | ||||||
|  | require_once 'common.php'; | ||||||
|  | require_once 'config.php'; | ||||||
|  | require_once 'config.local.php'; | ||||||
|  | require_once SYSTEM . 'functions.php'; | ||||||
|  | require_once SYSTEM . 'init.php'; | ||||||
|  | require_once SYSTEM . 'status.php'; | ||||||
|  |  | ||||||
|  | # error function | ||||||
|  | function sendError($message, $code = 3){ | ||||||
|  | 	$ret = []; | ||||||
|  | 	$ret['errorCode'] = $code; | ||||||
|  | 	$ret['errorMessage'] = $message; | ||||||
|  | 	die(json_encode($ret)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | # event schedule function | ||||||
|  | function parseEvent($table1, $date, $table2) | ||||||
|  | { | ||||||
|  | 	if ($table1) { | ||||||
|  | 		if ($date) { | ||||||
|  | 			if ($table2) { | ||||||
|  | 				$date = $table1->getAttribute('startdate'); | ||||||
|  | 				return date_create("{$date}")->format('U'); | ||||||
|  | 			} else { | ||||||
|  | 				$date = $table1->getAttribute('enddate'); | ||||||
|  | 				return date_create("{$date}")->format('U'); | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			foreach($table1 as $attr) { | ||||||
|  | 				if ($attr) { | ||||||
|  | 					return $attr->getAttribute($table2); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 'error'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | $request = json_decode(file_get_contents('php://input')); | ||||||
|  | $action = $request->type ?? ''; | ||||||
|  |  | ||||||
|  | /** @var OTS_Base_DB $db */ | ||||||
|  | /** @var array $config */ | ||||||
|  |  | ||||||
|  | switch ($action) { | ||||||
|  | 	case 'cacheinfo': | ||||||
|  | 		$playersonline = $db->query("select count(*) from `players_online`")->fetchAll(); | ||||||
|  | 		die(json_encode([ | ||||||
|  | 			'playersonline' => (intval($playersonline[0][0])), | ||||||
|  | 			'twitchstreams' => 0, | ||||||
|  | 			'twitchviewer' => 0, | ||||||
|  | 			'gamingyoutubestreams' => 0, | ||||||
|  | 			'gamingyoutubeviewer' => 0 | ||||||
|  | 		])); | ||||||
|  |  | ||||||
|  | 	case 'eventschedule': | ||||||
|  | 		$eventlist = []; | ||||||
|  | 		$file_path = config('server_path') . 'data/XML/events.xml'; | ||||||
|  | 		if (!file_exists($file_path)) { | ||||||
|  | 			die(json_encode([])); | ||||||
|  | 		} | ||||||
|  | 		$xml = new DOMDocument; | ||||||
|  | 		$xml->load($file_path); | ||||||
|  | 		$tmplist = []; | ||||||
|  | 		$tableevent = $xml->getElementsByTagName('event'); | ||||||
|  |  | ||||||
|  | 		foreach ($tableevent as $event) { | ||||||
|  | 			if ($event) { $tmplist = [ | ||||||
|  | 			'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'), | ||||||
|  | 			'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'), | ||||||
|  | 			'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'), | ||||||
|  | 			'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')), | ||||||
|  | 			'enddate' => intval(parseEvent($event, true, false)), | ||||||
|  | 			'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))), | ||||||
|  | 			'name' => $event->getAttribute('name'), | ||||||
|  | 			'startdate' => intval(parseEvent($event, true, true)), | ||||||
|  | 			'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent')) | ||||||
|  | 				]; | ||||||
|  | 			$eventlist[] = $tmplist; } } | ||||||
|  | 		die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()])); | ||||||
|  |  | ||||||
|  | 	case 'boostedcreature': | ||||||
|  | 		$boostDB = $db->query("select * from " . $db->tableName('boosted_creature'))->fetchAll(); | ||||||
|  | 		foreach ($boostDB as $Tableboost) { | ||||||
|  | 		die(json_encode([ | ||||||
|  | 			'boostedcreature' => true, | ||||||
|  | 			'raceid' => intval($Tableboost['raceid']) | ||||||
|  | 		])); | ||||||
|  | 		} | ||||||
|  | 	break; | ||||||
|  |  | ||||||
|  | 	case 'login': | ||||||
|  |  | ||||||
|  | 		$port = $config['lua']['gameProtocolPort']; | ||||||
|  |  | ||||||
|  | 		// default world info | ||||||
|  | 		$world = [ | ||||||
|  | 			'id' => 0, | ||||||
|  | 			'name' => $config['lua']['serverName'], | ||||||
|  | 			'externaladdress' => $config['lua']['ip'], | ||||||
|  | 			'externalport' => $port, | ||||||
|  | 			'externaladdressprotected' => $config['lua']['ip'], | ||||||
|  | 			'externalportprotected' => $port, | ||||||
|  | 			'externaladdressunprotected' => $config['lua']['ip'], | ||||||
|  | 			'externalportunprotected' => $port, | ||||||
|  | 			'previewstate' => 0, | ||||||
|  | 			'location' => 'BRA', // BRA, EUR, USA | ||||||
|  | 			'anticheatprotection' => false, | ||||||
|  | 			'pvptype' => array_search($config['lua']['worldType'], ['pvp', 'no-pvp', 'pvp-enforced']), | ||||||
|  | 			'istournamentworld' => false, | ||||||
|  | 			'restrictedstore' => false, | ||||||
|  | 			'currenttournamentphase' => 2 | ||||||
|  | 		]; | ||||||
|  |  | ||||||
|  | 		$characters = []; | ||||||
|  | 		$account = new OTS_Account(); | ||||||
|  |  | ||||||
|  | 		$inputEmail = $request->email ?? false; | ||||||
|  | 		$inputAccountName = $request->accountname ?? false; | ||||||
|  | 		$inputToken = $request->token ?? false; | ||||||
|  |  | ||||||
|  | 		if ($inputEmail != false) { // login by email | ||||||
|  | 			$account->findByEmail($request->email); | ||||||
|  | 		} | ||||||
|  | 		else if($inputAccountName != false) { // login by account name | ||||||
|  | 			$account->find($inputAccountName); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$config_salt_enabled = fieldExist('salt', 'accounts'); | ||||||
|  | 		$current_password = encrypt(($config_salt_enabled ? $account->getCustomField('salt') : '') . $request->password); | ||||||
|  |  | ||||||
|  | 		if (!$account->isLoaded() || $account->getPassword() != $current_password) { | ||||||
|  | 			sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		//log_append('test.log', var_export($account->getCustomField('secret'), true)); | ||||||
|  | 		$accountHasSecret = false; | ||||||
|  | 		if (fieldExist('secret', 'accounts')) { | ||||||
|  | 			$accountSecret = $account->getCustomField('secret'); | ||||||
|  | 			if ($accountSecret != null && $accountSecret != '') { | ||||||
|  | 				$accountHasSecret = true; | ||||||
|  | 				if ($inputToken === false) { | ||||||
|  | 					sendError('Submit a valid two-factor authentication token.', 6); | ||||||
|  | 				} else { | ||||||
|  | 					require_once LIBS . 'rfc6238.php'; | ||||||
|  | 					if (TokenAuth6238::verify($accountSecret, $inputToken) !== true) { | ||||||
|  | 						sendError('Two-factor authentication failed, token is wrong.', 6); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// common columns | ||||||
|  | 		$columns = 'id, name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons'; | ||||||
|  |  | ||||||
|  | 		if (fieldExist('isreward', 'accounts')) { | ||||||
|  | 			$columns .= ', isreward'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (fieldExist('istutorial', 'accounts')) { | ||||||
|  | 			$columns .= ', istutorial'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$players = $db->query("select {$columns} from players where account_id = " . $account->getId() . " AND deletion = 0"); | ||||||
|  | 		if($players && $players->rowCount() > 0) { | ||||||
|  | 			$players = $players->fetchAll(); | ||||||
|  |  | ||||||
|  | 			$highestLevelId = 0; | ||||||
|  | 			$highestLevel = 0; | ||||||
|  | 			foreach ($players as $player) { | ||||||
|  | 				if ($player['level'] >= $highestLevel) { | ||||||
|  | 					$highestLevel = $player['level']; | ||||||
|  | 					$highestLevelId = $player['id']; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			foreach ($players as $player) { | ||||||
|  | 				$characters[] = create_char($player, $highestLevelId); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) { | ||||||
|  | 			$save = false; | ||||||
|  | 			$timeNow = time(); | ||||||
|  | 			$query = $db->query("select `premdays`, `lastday` from `accounts` where `id` = " . $account->getId()); | ||||||
|  | 			if ($query->rowCount() > 0) { | ||||||
|  | 				$query = $query->fetch(); | ||||||
|  | 				$premDays = (int)$query['premdays']; | ||||||
|  | 				$lastDay = (int)$query['lastday']; | ||||||
|  | 				$lastLogin = $lastDay; | ||||||
|  | 			} else { | ||||||
|  | 				sendError("Error while fetching your account data. Please contact admin."); | ||||||
|  | 			} | ||||||
|  | 			if ($premDays != 0 && $premDays != PHP_INT_MAX) { | ||||||
|  | 				if ($lastDay == 0) { | ||||||
|  | 					$lastDay = $timeNow; | ||||||
|  | 					$save = true; | ||||||
|  | 				} else { | ||||||
|  | 					$days = (int)(($timeNow - $lastDay) / 86400); | ||||||
|  | 					if ($days > 0) { | ||||||
|  | 						if ($days >= $premDays) { | ||||||
|  | 							$premDays = 0; | ||||||
|  | 							$lastDay = 0; | ||||||
|  | 						} else { | ||||||
|  | 							$premDays -= $days; | ||||||
|  | 							$reminder = ($timeNow - $lastDay) % 86400; | ||||||
|  | 							$lastDay = $timeNow - $reminder; | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 						$save = true; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} else if ($lastDay != 0) { | ||||||
|  | 				$lastDay = 0; | ||||||
|  | 				$save = true; | ||||||
|  | 			} | ||||||
|  | 			if ($save) { | ||||||
|  | 				$db->query("update `accounts` set `premdays` = " . $premDays . ", `lastday` = " . $lastDay . " where `id` = " . $account->getId()); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$worlds = [$world]; | ||||||
|  | 		$playdata = compact('worlds', 'characters'); | ||||||
|  |  | ||||||
|  | 		$sessionKey = ($inputEmail !== false) ? $inputEmail : $inputAccountName; // email or account name | ||||||
|  | 		$sessionKey .= "\n" . $request->password; // password | ||||||
|  | 		if (!fieldExist('istutorial', 'players')) { | ||||||
|  | 			$sessionKey .= "\n"; | ||||||
|  | 		} | ||||||
|  | 		$sessionKey .= ($accountHasSecret && strlen($accountSecret) > 5) ? $inputToken : ''; | ||||||
|  |  | ||||||
|  | 		// this is workaround to distinguish between TFS 1.x and otservbr | ||||||
|  | 		// TFS 1.x requires the number in session key | ||||||
|  | 		// otservbr requires just login and password | ||||||
|  | 		// so we check for istutorial field which is present in otservbr, and not in TFS | ||||||
|  | 		if (!fieldExist('istutorial', 'players')) { | ||||||
|  | 			$sessionKey .= "\n".floor(time() / 30); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		//log_append('slaw.log', $sessionKey); | ||||||
|  |  | ||||||
|  | 		$session = [ | ||||||
|  | 			'sessionkey' => $sessionKey, | ||||||
|  | 			'lastlogintime' => 0, | ||||||
|  | 			'ispremium' => $config['lua']['freePremium'] || $account->isPremium(), | ||||||
|  | 			'premiumuntil' => ($account->getPremDays()) > 0 ? (time() + ($account->getPremDays() * 86400)) : 0, | ||||||
|  | 			'status' => 'active', // active, frozen or suspended | ||||||
|  | 			'returnernotification' => false, | ||||||
|  | 			'showrewardnews' => true, | ||||||
|  | 			'isreturner' => true, | ||||||
|  | 			'fpstracking' => false, | ||||||
|  | 			'optiontracking' => false, | ||||||
|  | 			'tournamentticketpurchasestate' => 0, | ||||||
|  | 			'emailcoderequest' => false | ||||||
|  | 		]; | ||||||
|  | 		die(json_encode(compact('session', 'playdata'))); | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		sendError("Unrecognized event {$action}."); | ||||||
|  | 	break; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function create_char($player, $highestLevelId) { | ||||||
|  | 	global $config; | ||||||
|  | 	return [ | ||||||
|  | 		'worldid' => 0, | ||||||
|  | 		'name' => $player['name'], | ||||||
|  | 		'ismale' => intval($player['sex']) === 1, | ||||||
|  | 		'tutorial' => isset($player['istutorial']) && $player['istutorial'], | ||||||
|  | 		'level' => intval($player['level']), | ||||||
|  | 		'vocation' => $config['vocations'][$player['vocation']], | ||||||
|  | 		'outfitid' => intval($player['looktype']), | ||||||
|  | 		'headcolor' => intval($player['lookhead']), | ||||||
|  | 		'torsocolor' => intval($player['lookbody']), | ||||||
|  | 		'legscolor' => intval($player['looklegs']), | ||||||
|  | 		'detailcolor' => intval($player['lookfeet']), | ||||||
|  | 		'addonsflags' => intval($player['lookaddons']), | ||||||
|  | 		'ishidden' => isset($player['deletion']) && (int)$player['deletion'] === 1, | ||||||
|  | 		'istournamentparticipant' => false, | ||||||
|  | 		'ismaincharacter' => $highestLevelId == $player['id'], | ||||||
|  | 		'dailyrewardstate' => isset($player['isreward']) ? intval($player['isreward']) : 0, | ||||||
|  | 		'remainingdailytournamentplaytime' => 0 | ||||||
|  | 	]; | ||||||
|  | } | ||||||
| @@ -1,25 +1,44 @@ | |||||||
| server { | server { | ||||||
|         listen 80; | 	listen 80; | ||||||
|         root /home/otserv/www/public; | 	root /home/otserv/www/public; | ||||||
|         index index.php; | 	index index.php; | ||||||
|         server_name your-domain.com; | 	server_name your-domain.com; | ||||||
|  |  | ||||||
|         location / { | 	# increase max file upload | ||||||
|                 try_files $uri $uri/ /index.php; | 	client_max_body_size 10M; | ||||||
|         } |  | ||||||
|  |  | ||||||
|         location ~ \.php$ { | 	# this is very important, be sure its in your nginx conf - it prevents access to logs etc. | ||||||
|                 include snippets/fastcgi-php.conf; | 	location ~ /system { | ||||||
|                 fastcgi_read_timeout 240; | 		deny all; | ||||||
|                 fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; | 		return 404; | ||||||
|         } | 	} | ||||||
|  |  | ||||||
|         location ~ /\.ht { | 	location /vendor { | ||||||
|                 deny all; | 		deny all; | ||||||
|         } | 		return 404; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|         location /system { | 	# block .htaccess, CHANGELOG.md, composer.json etc. | ||||||
|            deny all; | 	# this is to prevent finding software versions | ||||||
|            return 404; | 	location ~\.(ht|md|json|dist)$ { | ||||||
|         } | 		deny all; | ||||||
|  | 		return 404; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	# block git files and folders | ||||||
|  | 	location ~ /\.git { | ||||||
|  | 		return 404; | ||||||
|  | 		deny all; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	location / { | ||||||
|  | 		try_files $uri $uri/ /index.php?$query_string;; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	location ~ \.php$ { | ||||||
|  | 		include snippets/fastcgi-php.conf; | ||||||
|  | 		fastcgi_read_timeout 240; | ||||||
|  | 		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; | ||||||
|  | 		# for ubuntu 22.04+ it will be php8.1-fpm.sock | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,3 @@ | |||||||
| <IfModule mod_autoindex.c> | <IfModule mod_autoindex.c> | ||||||
| 	Options -Indexes | 	Options -Indexes | ||||||
| </IfModule> | </IfModule> | ||||||
|  |  | ||||||
| <IfVersion < 2.4> |  | ||||||
|     order allow,deny |  | ||||||
|     deny from all |  | ||||||
| </IfVersion> |  | ||||||
| <IfVersion >= 2.4> |  | ||||||
|     Require all denied |  | ||||||
| </IfVersion> |  | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								plugins/email-confirmed-reward.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								plugins/email-confirmed-reward.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | { | ||||||
|  | 	"name": "EMail Confirmed Reward", | ||||||
|  | 	"description": "Reward users for confirming their E-Mail.", | ||||||
|  | 	"version": "1.0", | ||||||
|  | 	"author": "MyAAC Authors", | ||||||
|  | 	"contact": "www.my-aac.org", | ||||||
|  | 	"hooks": { | ||||||
|  | 		"mail-confirmed-reward": { | ||||||
|  | 			"type": "EMAIL_CONFIRMED", | ||||||
|  | 			"file": "plugins/email-confirmed-reward/reward.php" | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 	"uninstall": [ | ||||||
|  | 		"plugins/email-confirmed-reward.json", | ||||||
|  | 		"plugins/email-confirmed-reward" | ||||||
|  | 	] | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								plugins/email-confirmed-reward/reward.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								plugins/email-confirmed-reward/reward.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | <?php | ||||||
|  | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
|  | $reward = config('account_mail_confirmed_reward'); | ||||||
|  |  | ||||||
|  | $hasCoinsColumn = $db->hasColumn('accounts', 'coins'); | ||||||
|  | if ($reward['coins'] > 0 && !$hasCoinsColumn) { | ||||||
|  | 	log_append('email_confirm_error.log', 'accounts.coins column does not exist.'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (!isset($account) || !$account->isLoaded()) { | ||||||
|  | 	//log_append('email_confirm_error.log', 'Account not loaded.'); | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if ($reward['premium_points'] > 0) { | ||||||
|  | 	$account->setCustomField('premium_points', (int)$account->getCustomField('premium_points') + $reward['premium_points']); | ||||||
|  |  | ||||||
|  | 	success(sprintf($reward['message'], $reward['premium_points'], 'premium points')); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if ($reward['coins'] > 0 && $hasCoinsColumn) { | ||||||
|  | 	$account->setCustomField('coins', (int)$account->getCustomField('coins') + $reward['coins']); | ||||||
|  |  | ||||||
|  | 	success(sprintf($reward['message'], $reward['coins'], 'coins')); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if ($reward['premium_days'] > 0) { | ||||||
|  | 	$account->setPremDays($account->getPremDays() + $reward['premium_days']); | ||||||
|  | 	$account->save(); | ||||||
|  |  | ||||||
|  | 	success(sprintf($reward['message'], $reward['premium_days'], 'premium days')); | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								release.sh
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								release.sh
									
									
									
									
									
								
							| @@ -13,16 +13,18 @@ fi | |||||||
|  |  | ||||||
| if [ $1 = "prepare" ]; then | if [ $1 = "prepare" ]; then | ||||||
| 	# define release version | 	# define release version | ||||||
| 	version=`cat VERSION` | 	version=`php system/get_version_for_release.php` | ||||||
|  |  | ||||||
| 	echo "Preparing to release version $version of the MyAAC Project!" | 	echo "Preparing to release version $version of the MyAAC Project!" | ||||||
|  |  | ||||||
|  | 	# make required directories | ||||||
|  | 	mkdir -p releases | ||||||
|  | 	mkdir -p tmp | ||||||
|  |  | ||||||
| 	# get myaac from git archive | 	# get myaac from git archive | ||||||
| 	git archive --format zip --output tmp/myaac.zip master | 	git archive --format zip --output tmp/myaac.zip master | ||||||
|  |  | ||||||
| 	# make required directories | 	cd tmp/ || exit | ||||||
| 	mkdir -p releases |  | ||||||
| 	mkdir -p tmp && cd tmp |  | ||||||
|  |  | ||||||
| 	dir="myaac-$version" | 	dir="myaac-$version" | ||||||
| 	if [ -d "$dir" ] ; then | 	if [ -d "$dir" ] ; then | ||||||
| @@ -39,9 +41,9 @@ fi | |||||||
|  |  | ||||||
| if [ $1 = "pack" ]; then | if [ $1 = "pack" ]; then | ||||||
| 	# define release version | 	# define release version | ||||||
| 	version=`cat VERSION` | 	version=`php system/get_version_for_release.php` | ||||||
|  |  | ||||||
| 	cd tmp | 	cd tmp || exit | ||||||
|  |  | ||||||
| 	# tar.gz | 	# tar.gz | ||||||
| 	echo "Creating .tar.gz package.." | 	echo "Creating .tar.gz package.." | ||||||
| @@ -60,4 +62,4 @@ if [ $1 = "pack" ]; then | |||||||
| 	echo "Done. Released files can be found in 'releases' directory." | 	echo "Done. Released files can be found in 'releases' directory." | ||||||
|  |  | ||||||
| 	exit | 	exit | ||||||
| fi | fi | ||||||
|   | |||||||
| @@ -9,6 +9,11 @@ $loader->register(); | |||||||
| // register the base directories for the namespace prefix | // register the base directories for the namespace prefix | ||||||
| $loader->addNamespace('Composer\Semver', LIBS . 'semver'); | $loader->addNamespace('Composer\Semver', LIBS . 'semver'); | ||||||
| $loader->addNamespace('Twig', LIBS . 'Twig'); | $loader->addNamespace('Twig', LIBS . 'Twig'); | ||||||
|  | $loader->addNamespace('Symfony\Polyfill\Mbstring', LIBS . 'polyfill-mbstring'); | ||||||
|  |  | ||||||
|  | // load polyfill-mbstring bootstrap | ||||||
|  | require LIBS . 'polyfill-mbstring/bootstrap.php'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * An example of a general-purpose implementation that includes the optional |  * An example of a general-purpose implementation that includes the optional | ||||||
|  * functionality of allowing multiple base directories for a single namespace |  * functionality of allowing multiple base directories for a single namespace | ||||||
| @@ -203,4 +208,4 @@ class Psr4AutoloaderClass | |||||||
| 		} | 		} | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
|  */ |  */ | ||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
| $config['clients'] = array( | $config['clients'] = [ | ||||||
| 	710, | 	710, | ||||||
| 	740, | 	740, | ||||||
| 	750, | 	750, | ||||||
| @@ -54,7 +54,9 @@ $config['clients'] = array( | |||||||
|  |  | ||||||
| 	1000, | 	1000, | ||||||
| 	1010, | 	1010, | ||||||
|  | 	1020, | ||||||
| 	1021, | 	1021, | ||||||
|  | 	1030, | ||||||
| 	1031, | 	1031, | ||||||
| 	1034, | 	1034, | ||||||
| 	1041, | 	1041, | ||||||
| @@ -62,6 +64,7 @@ $config['clients'] = array( | |||||||
| 	1053, | 	1053, | ||||||
| 	1054, | 	1054, | ||||||
| 	1058, | 	1058, | ||||||
|  | 	1070, | ||||||
| 	1075, | 	1075, | ||||||
| 	1077, | 	1077, | ||||||
| 	1079, | 	1079, | ||||||
| @@ -73,6 +76,33 @@ $config['clients'] = array( | |||||||
| 	1096, | 	1096, | ||||||
| 	1097, | 	1097, | ||||||
| 	1098, | 	1098, | ||||||
|  |  | ||||||
| 	1100, | 	1100, | ||||||
| ); | 	1102, | ||||||
| ?> | 	1140, | ||||||
|  | 	1150, | ||||||
|  | 	1180, | ||||||
|  |  | ||||||
|  | 	1200, | ||||||
|  | 	1202, | ||||||
|  | 	1215, | ||||||
|  | 	1220, | ||||||
|  | 	1230, | ||||||
|  | 	1240, | ||||||
|  | 	1251, | ||||||
|  | 	1260, | ||||||
|  | 	1270, | ||||||
|  | 	1280, | ||||||
|  | 	1285, | ||||||
|  | 	1286, | ||||||
|  | 	1290, | ||||||
|  | 	1291, | ||||||
|  |  | ||||||
|  | 	1300, | ||||||
|  | 	1310, | ||||||
|  | 	1311, | ||||||
|  | 	1312, | ||||||
|  | 	1316, | ||||||
|  | 	1320, | ||||||
|  | 	1321, | ||||||
|  | ]; | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								system/compat/classes.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								system/compat/classes.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Compat classes (backward support for Gesior AAC) | ||||||
|  |  * | ||||||
|  |  * @package   MyAAC | ||||||
|  |  * @author    Slawkens <slawkens@gmail.com> | ||||||
|  |  * @copyright 2022 MyAAC | ||||||
|  |  * @link      https://my-aac.org | ||||||
|  |  */ | ||||||
|  | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
|  | class Account extends OTS_Account { | ||||||
|  | 	public function loadById($id) { | ||||||
|  | 		$this->load($id); | ||||||
|  | 	} | ||||||
|  | 	public function loadByName($name) { | ||||||
|  | 		$this->find($name); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class Player extends OTS_Player { | ||||||
|  | 	public function loadById($id) { | ||||||
|  | 		$this->load($id); | ||||||
|  | 	} | ||||||
|  | 	public function loadByName($name) { | ||||||
|  | 		$this->find($name); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | class Guild extends OTS_Guild { | ||||||
|  | 	public function loadById($id) { | ||||||
|  | 		$this->load($id); | ||||||
|  | 	} | ||||||
|  | 	public function loadByName($name) { | ||||||
|  | 		$this->find($name); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | class GuildRank extends OTS_GuildRank {} | ||||||
|  | class House extends OTS_House {} | ||||||
| @@ -10,6 +10,14 @@ | |||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
| switch($page) | switch($page) | ||||||
| { | { | ||||||
|  | 	case 'adminpanel': | ||||||
|  | 		header('Location: ' . ADMIN_URL); | ||||||
|  | 		die; | ||||||
|  | 
 | ||||||
|  | 	case 'archive': | ||||||
|  | 		$page = 'newsarchive'; | ||||||
|  | 		break; | ||||||
|  | 
 | ||||||
| 	case 'whoisonline': | 	case 'whoisonline': | ||||||
| 		$page = 'online'; | 		$page = 'online'; | ||||||
| 		break; | 		break; | ||||||
| @@ -37,4 +45,3 @@ switch($page) | |||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
| } | } | ||||||
| ?>
 |  | ||||||
| @@ -51,4 +51,3 @@ else | |||||||
| 		updateDatabaseConfig('views_counter', $views_counter); // update counter | 		updateDatabaseConfig('views_counter', $views_counter); // update counter | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| ?> |  | ||||||
|   | |||||||
| @@ -116,5 +116,4 @@ defined('MYAAC') or die('Direct access not allowed!'); | |||||||
| 				'<li>MySQL is not configured propertly in <i>config.lua</i>.</li>' . | 				'<li>MySQL is not configured propertly in <i>config.lua</i>.</li>' . | ||||||
| 				'<li>MySQL server is not running.</li>' . | 				'<li>MySQL server is not running.</li>' . | ||||||
| 			'</ul>' . $error->getMessage()); | 			'</ul>' . $error->getMessage()); | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| @@ -23,6 +23,8 @@ function exception_handler($exception) { | |||||||
|  |  | ||||||
| 	$backtrace_formatted = nl2br($exception->getTraceAsString()); | 	$backtrace_formatted = nl2br($exception->getTraceAsString()); | ||||||
|  |  | ||||||
|  | 	$message = $message . "<br/><br/>File: {$exception->getFile()}<br/>Line: {$exception->getLine()}"; | ||||||
|  |  | ||||||
| 	// display basic error message without template | 	// display basic error message without template | ||||||
| 	// template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right | 	// template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right | ||||||
| 	$template_file = SYSTEM . 'templates/exception.html.twig'; | 	$template_file = SYSTEM . 'templates/exception.html.twig'; | ||||||
| @@ -39,7 +41,7 @@ function exception_handler($exception) { | |||||||
| 	// we just replace some values manually | 	// we just replace some values manually | ||||||
| 	// cause in case Twig throws exception, we can show it too | 	// cause in case Twig throws exception, we can show it too | ||||||
| 	$content = file_get_contents($template_file); | 	$content = file_get_contents($template_file); | ||||||
| 	$content = str_replace(array('{{ BASE_URL }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content); | 	$content = str_replace(array('{{ BASE_URL }}', '{{ exceptionClass }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, get_class($exception), $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content); | ||||||
|  |  | ||||||
| 	echo $content; | 	echo $content; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,18 +7,26 @@ | |||||||
|  * @copyright 2019 MyAAC |  * @copyright 2019 MyAAC | ||||||
|  * @link      https://my-aac.org |  * @link      https://my-aac.org | ||||||
|  */ |  */ | ||||||
|  | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
| use Twig\Loader\ArrayLoader as Twig_ArrayLoader; | use Twig\Loader\ArrayLoader as Twig_ArrayLoader; | ||||||
|  |  | ||||||
| defined('MYAAC') or die('Direct access not allowed!'); |  | ||||||
|  |  | ||||||
| function message($message, $type, $return) | function message($message, $type, $return) | ||||||
| { | { | ||||||
|     if($return) | 	if(IS_CLI) { | ||||||
|         return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>'; | 		if($return) { | ||||||
|  | 			return $message; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|     echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>'; | 		echo $message; | ||||||
|     return true; | 		return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if($return) | ||||||
|  | 		return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>'; | ||||||
|  |  | ||||||
|  | 	echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>'; | ||||||
|  | 	return true; | ||||||
| } | } | ||||||
| function success($message, $return = false) { | function success($message, $return = false) { | ||||||
|     return message($message, 'success', $return); |     return message($message, 'success', $return); | ||||||
| @@ -442,7 +450,7 @@ function tickers() | |||||||
|  */ |  */ | ||||||
| function template_place_holder($type) | function template_place_holder($type) | ||||||
| { | { | ||||||
| 	global $template_place_holders; | 	global $twig, $template_place_holders; | ||||||
| 	$ret = ''; | 	$ret = ''; | ||||||
|  |  | ||||||
| 	if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type])) | 	if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type])) | ||||||
| @@ -451,6 +459,9 @@ function template_place_holder($type) | |||||||
| 	if($type === 'head_start') { | 	if($type === 'head_start') { | ||||||
| 		$ret .= template_header(); | 		$ret .= template_header(); | ||||||
| 	} | 	} | ||||||
|  | 	elseif ($type === 'body_start') { | ||||||
|  | 		$ret .= $twig->render('browsehappy.html.twig'); | ||||||
|  | 	} | ||||||
| 	elseif($type === 'body_end') { | 	elseif($type === 'body_end') { | ||||||
| 		$ret .= template_ga_code(); | 		$ret .= template_ga_code(); | ||||||
| 	} | 	} | ||||||
| @@ -745,10 +756,10 @@ function get_browser_languages() | |||||||
| { | { | ||||||
| 	$ret = array(); | 	$ret = array(); | ||||||
|  |  | ||||||
| 	$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE']; | 	if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) | ||||||
| 	if(!isset($acceptLang[0])) |  | ||||||
| 		return $ret; | 		return $ret; | ||||||
|  |  | ||||||
|  | 	$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE']; | ||||||
| 	$languages = strtolower($acceptLang); | 	$languages = strtolower($acceptLang); | ||||||
| 	// $languages = 'pl,en-us;q=0.7,en;q=0.3 '; | 	// $languages = 'pl,en-us;q=0.7,en;q=0.3 '; | ||||||
| 	// need to remove spaces from strings to avoid error | 	// need to remove spaces from strings to avoid error | ||||||
| @@ -786,7 +797,7 @@ function get_plugins() | |||||||
| 	$ret = array(); | 	$ret = array(); | ||||||
|  |  | ||||||
| 	$path = PLUGINS; | 	$path = PLUGINS; | ||||||
| 	foreach(scandir($path, 0) as $file) { | 	foreach(scandir($path, SCANDIR_SORT_ASCENDING) as $file) { | ||||||
| 		$file_ext = pathinfo($file, PATHINFO_EXTENSION); | 		$file_ext = pathinfo($file, PATHINFO_EXTENSION); | ||||||
| 		$file_name = pathinfo($file, PATHINFO_FILENAME); | 		$file_name = pathinfo($file, PATHINFO_FILENAME); | ||||||
| 		if ($file === '.' || $file === '..' || $file === 'disabled' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) | 		if ($file === '.' || $file === '..' || $file === 'disabled' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) | ||||||
| @@ -912,8 +923,8 @@ function load_config_lua($filename) | |||||||
| 	$config_file = $filename; | 	$config_file = $filename; | ||||||
| 	if(!@file_exists($config_file)) | 	if(!@file_exists($config_file)) | ||||||
| 	{ | 	{ | ||||||
| 		log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $filename . '). Error: ' . print_r(error_get_last(), true)); | 		log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $filename . ').'); | ||||||
| 		throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file. More info in system/logs/error.log'); | 		throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file.'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	$result = array(); | 	$result = array(); | ||||||
| @@ -922,6 +933,12 @@ function load_config_lua($filename) | |||||||
| 	if(count($lines) > 0) { | 	if(count($lines) > 0) { | ||||||
| 		foreach($lines as $ln => $line) | 		foreach($lines as $ln => $line) | ||||||
| 		{ | 		{ | ||||||
|  | 			$line = trim($line); | ||||||
|  | 			if(@$line[0] === '{' || @$line[0] === '}') { | ||||||
|  | 				// arrays are not supported yet | ||||||
|  | 				// just ignore the error | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			$tmp_exp = explode('=', $line, 2); | 			$tmp_exp = explode('=', $line, 2); | ||||||
| 			if(strpos($line, 'dofile') !== false) | 			if(strpos($line, 'dofile') !== false) | ||||||
| 			{ | 			{ | ||||||
| @@ -948,16 +965,17 @@ function load_config_lua($filename) | |||||||
| 						$result[$key] = (string) substr(substr($value, 1), 0, -1); | 						$result[$key] = (string) substr(substr($value, 1), 0, -1); | ||||||
| 					elseif(in_array($value, array('true', 'false'))) | 					elseif(in_array($value, array('true', 'false'))) | ||||||
| 						$result[$key] = ($value === 'true') ? true : false; | 						$result[$key] = ($value === 'true') ? true : false; | ||||||
| 					elseif(@$value[0] === '{' && @$value[strlen($value) - 1] === '}') { | 					elseif(@$value[0] === '{') { | ||||||
| 						// arrays are not supported yet | 						// arrays are not supported yet | ||||||
| 						// just ignore the error | 						// just ignore the error | ||||||
|  | 						continue; | ||||||
| 					} | 					} | ||||||
| 					else | 					else | ||||||
| 					{ | 					{ | ||||||
| 						foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull | 						foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull | ||||||
| 							$value = str_replace($tmp_key, $tmp_value, $value); | 							$value = str_replace($tmp_key, $tmp_value, $value); | ||||||
| 						$ret = @eval("return $value;"); | 						$ret = @eval("return $value;"); | ||||||
| 						if((string) $ret == '') // = parser error | 						if((string) $ret == '' && trim($value) !== '""') // = parser error | ||||||
| 						{ | 						{ | ||||||
| 							throw new RuntimeException('ERROR: Loading config.lua file. Line <b>' . ($ln + 1) . '</b> of LUA config file is not valid [key: <b>' . $key . '</b>]'); | 							throw new RuntimeException('ERROR: Loading config.lua file. Line <b>' . ($ln + 1) . '</b> of LUA config file is not valid [key: <b>' . $key . '</b>]'); | ||||||
| 						} | 						} | ||||||
| @@ -982,6 +1000,10 @@ function str_replace_first($search, $replace, $subject) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function get_browser_real_ip() { | function get_browser_real_ip() { | ||||||
|  | 	if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { | ||||||
|  | 		$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR'])) | 	if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR'])) | ||||||
| 		return $_SERVER['REMOTE_ADDR']; | 		return $_SERVER['REMOTE_ADDR']; | ||||||
| 	else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP'])) | 	else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP'])) | ||||||
| @@ -1019,7 +1041,7 @@ function getTopPlayers($limit = 5) { | |||||||
| 			$deleted = 'deletion'; | 			$deleted = 'deletion'; | ||||||
|  |  | ||||||
| 		$is_tfs10 = $db->hasTable('players_online'); | 		$is_tfs10 = $db->hasTable('players_online'); | ||||||
| 		$players = $db->query('SELECT `id`, `name`, `level`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll(); | 		$players = $db->query('SELECT `id`, `name`, `level`, `vocation`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll(); | ||||||
|  |  | ||||||
| 		if($is_tfs10) { | 		if($is_tfs10) { | ||||||
| 			foreach($players as &$player) { | 			foreach($players as &$player) { | ||||||
| @@ -1124,9 +1146,30 @@ function clearCache() | |||||||
| 		if ($cache->fetch('failed_logins', $tmp)) | 		if ($cache->fetch('failed_logins', $tmp)) | ||||||
| 			$cache->delete('failed_logins'); | 			$cache->delete('failed_logins'); | ||||||
|  |  | ||||||
| 		global $template_name; | 		foreach (get_templates() as $template) { | ||||||
| 		if ($cache->fetch('template_ini' . $template_name, $tmp)) | 			if ($cache->fetch('template_ini_' . $template, $tmp)) { | ||||||
| 			$cache->delete('template_ini' . $template_name); | 				$cache->delete('template_ini_' . $template); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ($cache->fetch('template_menus', $tmp)) { | ||||||
|  | 			$cache->delete('template_menus'); | ||||||
|  | 		} | ||||||
|  | 		if ($cache->fetch('database_tables', $tmp)) { | ||||||
|  | 			$cache->delete('database_tables'); | ||||||
|  | 		} | ||||||
|  | 		if ($cache->fetch('database_columns', $tmp)) { | ||||||
|  | 			$cache->delete('database_columns'); | ||||||
|  | 		} | ||||||
|  | 		if ($cache->fetch('database_checksum', $tmp)) { | ||||||
|  | 			$cache->delete('database_checksum'); | ||||||
|  | 		} | ||||||
|  | 		if ($cache->fetch('hooks', $tmp)) { | ||||||
|  | 			$cache->delete('hooks'); | ||||||
|  | 		} | ||||||
|  | 		if ($cache->fetch('last_kills', $tmp)) { | ||||||
|  | 			$cache->delete('last_kills'); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	deleteDirectory(CACHE . 'signatures', ['index.html'], true); | 	deleteDirectory(CACHE . 'signatures', ['index.html'], true); | ||||||
| @@ -1221,6 +1264,40 @@ function getCustomPage($page, &$success) | |||||||
| 	return $content; | 	return $content; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function escapeHtml($html) { | ||||||
|  | 	return htmlspecialchars($html); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function displayErrorBoxWithBackButton($errors, $action = null) { | ||||||
|  | 	global $twig; | ||||||
|  | 	$twig->display('error_box.html.twig', ['errors' => $errors]); | ||||||
|  | 	$twig->display('account.back_button.html.twig', [ | ||||||
|  | 		'action' => $action ?: getLink('') | ||||||
|  | 	]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getDatabasePages($withHidden = false): array | ||||||
|  | { | ||||||
|  | 	global $db, $logged_access; | ||||||
|  |  | ||||||
|  | 	if (!isset($logged_access)) { | ||||||
|  | 		$logged_access = 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	$pages = $db->query('SELECT `name` FROM ' . TABLE_PREFIX . 'pages WHERE ' . ($withHidden ? '' : '`hidden` != 1 AND ') . '`access` <= ' . $db->quote($logged_access)); | ||||||
|  | 	$ret = []; | ||||||
|  |  | ||||||
|  | 	if ($pages->rowCount() < 1) { | ||||||
|  | 		return $ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	foreach($pages->fetchAll() as $page) { | ||||||
|  | 		$ret[] = $page['name']; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return $ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| // validator functions | // validator functions | ||||||
| require_once LIBS . 'validator.php'; | require_once LIBS . 'validator.php'; | ||||||
| require_once SYSTEM . 'compat.php'; | require_once SYSTEM . 'compat/base.php'; | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								system/get_version_for_release.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								system/get_version_for_release.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | require __DIR__ . '/../common.php'; | ||||||
|  | if (IS_CLI) { | ||||||
|  | 	echo MYAAC_VERSION; | ||||||
|  | } | ||||||
| @@ -9,42 +9,50 @@ | |||||||
|  */ |  */ | ||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
| define('HOOK_STARTUP', 1); | $i = 0; | ||||||
| define('HOOK_BEFORE_PAGE', 2); | define('HOOK_STARTUP', ++$i); | ||||||
| define('HOOK_AFTER_PAGE', 3); | define('HOOK_BEFORE_PAGE', ++$i); | ||||||
| define('HOOK_FINISH', 4); | define('HOOK_AFTER_PAGE', ++$i); | ||||||
| define('HOOK_TIBIACOM_ARTICLE', 5); | define('HOOK_FINISH', ++$i); | ||||||
| define('HOOK_TIBIACOM_BORDER_3', 6); | define('HOOK_TIBIACOM_ARTICLE', ++$i); | ||||||
| define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', 7); | define('HOOK_TIBIACOM_BORDER_3', ++$i); | ||||||
| define('HOOK_CHARACTERS_AFTER_INFORMATIONS', 8); | define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', ++$i); | ||||||
| define('HOOK_CHARACTERS_BEFORE_SIGNATURE', 9); | define('HOOK_CHARACTERS_AFTER_INFORMATIONS', ++$i); | ||||||
| define('HOOK_CHARACTERS_AFTER_SIGNATURE', 10); | define('HOOK_CHARACTERS_BEFORE_SKILLS', ++$i); | ||||||
| define('HOOK_CHARACTERS_AFTER_ACCOUNT', 11); | define('HOOK_CHARACTERS_AFTER_SKILLS', ++$i); | ||||||
| define('HOOK_CHARACTERS_AFTER_CHARACTERS', 12); | define('HOOK_CHARACTERS_AFTER_QUESTS', ++$i); | ||||||
| define('HOOK_LOGIN', 13); | define('HOOK_CHARACTERS_AFTER_EQUIPMENT', ++$i); | ||||||
| define('HOOK_LOGIN_ATTEMPT', 14); | define('HOOK_CHARACTERS_BEFORE_DEATHS', ++$i); | ||||||
| define('HOOK_LOGOUT', 15); | define('HOOK_CHARACTERS_BEFORE_SIGNATURE', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', 16); | define('HOOK_CHARACTERS_AFTER_SIGNATURE', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', 17); | define('HOOK_CHARACTERS_AFTER_ACCOUNT', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', 18); | define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', 19); | define('HOOK_LOGIN', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', 20); | define('HOOK_LOGIN_ATTEMPT', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', 21); | define('HOOK_LOGOUT', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', 22); | define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', 23); | define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', 24); | define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', 25); | define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', 26); | define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', 27); | define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', 28); | define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_SEX', 29); | define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', 30); | define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', 31); | define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', 32); | define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_FORM', 33); | define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', ++$i); | ||||||
| define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', 34); | define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', ++$i); | ||||||
|  | define('HOOK_ACCOUNT_CREATE_AFTER_SEX', ++$i); | ||||||
|  | define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', ++$i); | ||||||
|  | define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', ++$i); | ||||||
|  | define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', ++$i); | ||||||
|  | define('HOOK_ACCOUNT_CREATE_AFTER_FORM', ++$i); | ||||||
|  | define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', ++$i); | ||||||
|  | define('HOOK_EMAIL_CONFIRMED', ++$i); | ||||||
|  | define('HOOK_GUILDS_AFTER_INVITED_CHARACTERS', ++$i); | ||||||
| define('HOOK_FIRST', HOOK_STARTUP); | define('HOOK_FIRST', HOOK_STARTUP); | ||||||
| define('HOOK_LAST', HOOK_ACCOUNT_CREATE_AFTER_SUBMIT); | define('HOOK_LAST', HOOK_EMAIL_CONFIRMED); | ||||||
|  |  | ||||||
| require_once LIBS . 'plugins.php'; | require_once LIBS . 'plugins.php'; | ||||||
| class Hook | class Hook | ||||||
| @@ -67,9 +75,7 @@ class Hook | |||||||
| 		}*/ | 		}*/ | ||||||
|  |  | ||||||
| 		global $db, $config, $template_path, $ots, $content, $twig; | 		global $db, $config, $template_path, $ots, $content, $twig; | ||||||
| 		if(file_exists(BASE . $this->_file)) { | 		$ret = include BASE . $this->_file; | ||||||
| 			$ret = require BASE . $this->_file; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return !isset($ret) || $ret == 1 || $ret; | 		return !isset($ret) || $ret == 1 || $ret; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -31,9 +31,6 @@ if($config['gzip_output'] && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($ | |||||||
| require_once SYSTEM . 'libs/cache.php'; | require_once SYSTEM . 'libs/cache.php'; | ||||||
| $cache = Cache::getInstance(); | $cache = Cache::getInstance(); | ||||||
|  |  | ||||||
| // twig |  | ||||||
| require_once SYSTEM . 'twig.php'; |  | ||||||
|  |  | ||||||
| // trim values we receive | // trim values we receive | ||||||
| if(isset($_POST)) | if(isset($_POST)) | ||||||
| { | { | ||||||
| @@ -114,16 +111,21 @@ if(!isset($foundValue)) { | |||||||
| $config['data_path'] = $foundValue; | $config['data_path'] = $foundValue; | ||||||
| unset($foundValue); | unset($foundValue); | ||||||
|  |  | ||||||
| // new config values for compability | // new config values for compatibility | ||||||
| if(!isset($config['highscores_ids_hidden']) || count($config['highscores_ids_hidden']) == 0) { | if(!isset($config['highscores_ids_hidden']) || count($config['highscores_ids_hidden']) == 0) { | ||||||
| 	$config['highscores_ids_hidden'] = array(0); | 	$config['highscores_ids_hidden'] = array(0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | $config['account_create_character_create'] = config('account_create_character_create') && (!config('mail_enabled') || !config('account_mail_verify')); | ||||||
|  |  | ||||||
| // POT | // POT | ||||||
| require_once SYSTEM . 'libs/pot/OTS.php'; | require_once SYSTEM . 'libs/pot/OTS.php'; | ||||||
| $ots = POT::getInstance(); | $ots = POT::getInstance(); | ||||||
| require_once SYSTEM . 'database.php'; | require_once SYSTEM . 'database.php'; | ||||||
|  |  | ||||||
|  | // twig | ||||||
|  | require_once SYSTEM . 'twig.php'; | ||||||
|  |  | ||||||
| define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); | define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); | ||||||
| // load vocation names | // load vocation names | ||||||
| $tmp = ''; | $tmp = ''; | ||||||
| @@ -140,10 +142,8 @@ else { | |||||||
| 	if(!@file_exists($file)) | 	if(!@file_exists($file)) | ||||||
| 		$file = $config['data_path'] . 'vocations.xml'; | 		$file = $config['data_path'] . 'vocations.xml'; | ||||||
|  |  | ||||||
| 	$vocations->load($file); | 	if(!$vocations->load($file)) | ||||||
|  | 		throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.'); | ||||||
| 	if(!$vocations) |  | ||||||
| 		throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> file.'); |  | ||||||
|  |  | ||||||
| 	$config['vocations'] = array(); | 	$config['vocations'] = array(); | ||||||
| 	foreach($vocations->getElementsByTagName('vocation') as $vocation) { | 	foreach($vocations->getElementsByTagName('vocation') as $vocation) { | ||||||
| @@ -180,7 +180,8 @@ else { | |||||||
| // load towns from database (TFS 1.3) // | // load towns from database (TFS 1.3) // | ||||||
| //////////////////////////////////////// | //////////////////////////////////////// | ||||||
|  |  | ||||||
| $towns = array(); | $tmp = ''; | ||||||
|  | $towns = []; | ||||||
| if($cache->enabled() && $cache->fetch('towns', $tmp)) { | if($cache->enabled() && $cache->fetch('towns', $tmp)) { | ||||||
| 	$towns = unserialize($tmp); | 	$towns = unserialize($tmp); | ||||||
| } | } | ||||||
| @@ -193,20 +194,14 @@ else { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		unset($query); | 		unset($query); | ||||||
| 		if($cache->enabled()) { |  | ||||||
| 			$cache->set('towns', serialize($towns), 600); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	else if($cache->enabled()) { | 	else { | ||||||
| 		$cache->set('towns', serialize(array()), 600); | 		$towns = config('towns'); | ||||||
| 	} | 	} | ||||||
| } |  | ||||||
|  |  | ||||||
| $configTowns = config('towns'); | 	if($cache->enabled()) { | ||||||
| if($configTowns !== null && (!isset($configTowns[1]) || $configTowns[1] !== 'Sample town')) { | 		$cache->set('towns', serialize($towns), 600); | ||||||
| 	$towns = array_replace( | 	} | ||||||
| 		$towns, $configTowns |  | ||||||
| 	); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| config(['towns', $towns]); | config(['towns', $towns]); | ||||||
|   | |||||||
| @@ -58,4 +58,3 @@ function outputItem($id = 100, $count = 1) | |||||||
| 	$file_name = Items_Images::$outputDir . $file_name . '.gif'; | 	$file_name = Items_Images::$outputDir . $file_name . '.gif'; | ||||||
| 	readfile($file_name); | 	readfile($file_name); | ||||||
| } | } | ||||||
| ?> |  | ||||||
|   | |||||||
| @@ -11,6 +11,57 @@ | |||||||
|  |  | ||||||
| class CreateCharacter | class CreateCharacter | ||||||
| { | { | ||||||
|  | 	/** | ||||||
|  | 	 * @param $name | ||||||
|  | 	 * @param $errors | ||||||
|  | 	 * @return bool | ||||||
|  | 	 */ | ||||||
|  | 	public function checkName($name, &$errors) | ||||||
|  | 	{ | ||||||
|  | 		$minLength = config('character_name_min_length'); | ||||||
|  | 		$maxLength = config('character_name_max_length'); | ||||||
|  |  | ||||||
|  | 		if(empty($name)) { | ||||||
|  | 			$errors['name'] = 'Please enter a name for your character!'; | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if(strlen($name) > $maxLength) { | ||||||
|  | 			$errors['name'] = 'Name is too long. Max. length <b>' . $maxLength . '</b> letters.'; | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if(strlen($name) < $minLength) { | ||||||
|  | 			$errors['name'] = 'Name is too short. Min. length <b>' . $minLength . '</b> letters.'; | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$name_length = strlen($name); | ||||||
|  | 		if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) { | ||||||
|  | 			$errors['name'] = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.'; | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if(!preg_match("/[A-z ']/", $name)) { | ||||||
|  | 			$errors['name'] = 'Your name contains illegal characters.'; | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if(!admin() && !Validator::newCharacterName($name)) { | ||||||
|  | 			$errors['name'] = Validator::getLastError(); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$player = new OTS_Player(); | ||||||
|  | 		$player->find($name); | ||||||
|  | 		if($player->isLoaded()) { | ||||||
|  | 			$errors['name'] = 'Character with this name already exist.'; | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return empty($errors); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * @param string $name | 	 * @param string $name | ||||||
| 	 * @param int $sex | 	 * @param int $sex | ||||||
| @@ -19,42 +70,27 @@ class CreateCharacter | |||||||
| 	 * @param array $errors | 	 * @param array $errors | ||||||
| 	 * @return bool | 	 * @return bool | ||||||
| 	 */ | 	 */ | ||||||
| 	public function check($name, $sex, &$vocation, &$town, &$errors) { | 	public function check($name, $sex, &$vocation, &$town, &$errors) | ||||||
| 		$minLength = config('character_name_min_length'); | 	{ | ||||||
| 		$maxLength = config('character_name_max_length'); | 		$this->checkName($name, $errors); | ||||||
|  |  | ||||||
| 		if(empty($name)) | 		if(empty($sex) && $sex != "0") { | ||||||
| 			$errors['name'] = 'Please enter a name for your character!'; |  | ||||||
| 		else if(strlen($name) > $maxLength) |  | ||||||
| 			$errors['name'] = 'Name is too long. Max. lenght <b>'.$maxLength.'</b> letters.'; |  | ||||||
| 		else if(strlen($name) < $minLength) |  | ||||||
| 			$errors['name'] = 'Name is too short. Min. lenght <b>'.$minLength.'</b> letters.'; |  | ||||||
| 		else { |  | ||||||
| 			if(!admin() && !Validator::newCharacterName($name)) { |  | ||||||
| 				$errors['name'] = Validator::getLastError(); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			$exist = new OTS_Player(); |  | ||||||
| 			$exist->find($name); |  | ||||||
| 			if($exist->isLoaded()) { |  | ||||||
| 				$errors['name'] = 'Character with this name already exist.'; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if(empty($sex) && $sex != "0") |  | ||||||
| 			$errors['sex'] = 'Please select the sex for your character!'; | 			$errors['sex'] = 'Please select the sex for your character!'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if(count(config('character_samples')) > 1) | 		if(count(config('character_samples')) > 1) | ||||||
| 		{ | 		{ | ||||||
| 			if(!isset($vocation)) | 			if(!isset($vocation)) | ||||||
| 				$errors['vocation'] = 'Please select a vocation for your character.'; | 				$errors['vocation'] = 'Please select a vocation for your character.'; | ||||||
| 		} | 		} | ||||||
| 		else | 		else { | ||||||
| 			$vocation = config('character_samples')[0]; | 			$vocation = config('character_samples')[0]; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if(count(config('character_towns')) > 1) { | 		if(count(config('character_towns')) > 1) { | ||||||
| 			if(!isset($town)) | 			if(!isset($town)) { | ||||||
| 				$errors['town'] = 'Please select a town for your character.'; | 				$errors['town'] = 'Please select a town for your character.'; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			$town = config('character_towns')[0]; | 			$town = config('character_towns')[0]; | ||||||
| @@ -102,7 +138,7 @@ class CreateCharacter | |||||||
|  |  | ||||||
| 		if(empty($errors)) | 		if(empty($errors)) | ||||||
| 		{ | 		{ | ||||||
| 			$number_of_players_on_account = $account->getPlayersList()->count(); | 			$number_of_players_on_account = $account->getPlayersList(true)->count(); | ||||||
| 			if($number_of_players_on_account >= config('characters_per_account')) | 			if($number_of_players_on_account >= config('characters_per_account')) | ||||||
| 				$errors[] = 'You have too many characters on your account <b>('.$number_of_players_on_account.'/'.config('characters_per_account').')</b>!'; | 				$errors[] = 'You have too many characters on your account <b>('.$number_of_players_on_account.'/'.config('characters_per_account').')</b>!'; | ||||||
| 		} | 		} | ||||||
| @@ -120,7 +156,7 @@ class CreateCharacter | |||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		global $db, $twig; | 		global $db; | ||||||
|  |  | ||||||
| 		if($sex == "0") | 		if($sex == "0") | ||||||
| 			$char_to_copy->setLookType(136); | 			$char_to_copy->setLookType(136); | ||||||
| @@ -157,8 +193,14 @@ class CreateCharacter | |||||||
| 		$player->setManaSpent($char_to_copy->getManaSpent()); | 		$player->setManaSpent($char_to_copy->getManaSpent()); | ||||||
| 		$player->setSoul($char_to_copy->getSoul()); | 		$player->setSoul($char_to_copy->getSoul()); | ||||||
|  |  | ||||||
| 		for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) | 		for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) { | ||||||
| 			$player->setSkill($skill, 10); | 			$value = 10; | ||||||
|  | 			if (config('use_character_sample_skills')) { | ||||||
|  | 				$value = $char_to_copy->getSkill($skill); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			$player->setSkill($skill, $value); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		$player->setLookBody($char_to_copy->getLookBody()); | 		$player->setLookBody($char_to_copy->getLookBody()); | ||||||
| 		$player->setLookFeet($char_to_copy->getLookFeet()); | 		$player->setLookFeet($char_to_copy->getLookFeet()); | ||||||
| @@ -186,7 +228,7 @@ class CreateCharacter | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		$player->save(); | 		$player->save(); | ||||||
| 		$player->setCustomField("created", time()); | 		$player->setCustomField('created', time()); | ||||||
|  |  | ||||||
| 		$player = new OTS_Player(); | 		$player = new OTS_Player(); | ||||||
| 		$player->find($name); | 		$player->find($name); | ||||||
| @@ -197,18 +239,28 @@ class CreateCharacter | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if($db->hasTable('player_skills')) { | 		if($db->hasTable('player_skills')) { | ||||||
|  |  | ||||||
| 			for($i=0; $i<7; $i++) { | 			for($i=0; $i<7; $i++) { | ||||||
|  | 				$value = 10; | ||||||
|  | 				if (config('use_character_sample_skills')) { | ||||||
|  | 					$value = $char_to_copy->getSkill($i); | ||||||
|  | 				} | ||||||
| 				$skillExists = $db->query('SELECT `skillid` FROM `player_skills` WHERE `player_id` = ' . $player->getId() . ' AND `skillid` = ' . $i); | 				$skillExists = $db->query('SELECT `skillid` FROM `player_skills` WHERE `player_id` = ' . $player->getId() . ' AND `skillid` = ' . $i); | ||||||
| 				if($skillExists->rowCount() <= 0) { | 				if($skillExists->rowCount() <= 0) { | ||||||
| 					$db->query('INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES ('.$player->getId().', '.$i.', 10, 0)'); | 					$db->query('INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES ('.$player->getId().', '.$i.', ' . $value . ', 0)'); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		$loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId().""); | 		if ($db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) { | ||||||
| 		foreach($loaded_items_to_copy as $save_item) | 			$loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId().""); | ||||||
| 			$db->query("INSERT INTO `player_items` (`player_id` ,`pid` ,`sid` ,`itemtype`, `count`, `attributes`) VALUES ('".$player->getId()."', '".$save_item['pid']."', '".$save_item['sid']."', '".$save_item['itemtype']."', '".$save_item['count']."', '".$save_item['attributes']."');"); | 			foreach($loaded_items_to_copy as $save_item) { | ||||||
|  | 				$blob = $db->quote($save_item['attributes']); | ||||||
|  | 				$db->query("INSERT INTO `player_items` (`player_id` ,`pid` ,`sid` ,`itemtype`, `count`, `attributes`) VALUES ('".$player->getId()."', '".$save_item['pid']."', '".$save_item['sid']."', '".$save_item['itemtype']."', '".$save_item['count']."', $blob);"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		global $twig; | ||||||
| 		$twig->display('success.html.twig', array( | 		$twig->display('success.html.twig', array( | ||||||
| 			'title' => 'Character Created', | 			'title' => 'Character Created', | ||||||
| 			'description' => 'The character <b>' . $name . '</b> has been created.<br/> | 			'description' => 'The character <b>' . $name . '</b> has been created.<br/> | ||||||
| @@ -219,4 +271,4 @@ class CreateCharacter | |||||||
| 		$account->logAction('Created character <b>' . $name . '</b>.'); | 		$account->logAction('Created character <b>' . $name . '</b>.'); | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ namespace Twig\Cache; | |||||||
|  */ |  */ | ||||||
| class FilesystemCache implements CacheInterface | class FilesystemCache implements CacheInterface | ||||||
| { | { | ||||||
|     const FORCE_BYTECODE_INVALIDATION = 1; |     public const FORCE_BYTECODE_INVALIDATION = 1; | ||||||
|  |  | ||||||
|     private $directory; |     private $directory; | ||||||
|     private $options; |     private $options; | ||||||
| @@ -35,7 +35,7 @@ class FilesystemCache implements CacheInterface | |||||||
|  |  | ||||||
|     public function generateKey($name, $className) |     public function generateKey($name, $className) | ||||||
|     { |     { | ||||||
|         $hash = hash('sha256', $className); |         $hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $className); | ||||||
|  |  | ||||||
|         return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php'; |         return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php'; | ||||||
|     } |     } | ||||||
| @@ -67,7 +67,7 @@ class FilesystemCache implements CacheInterface | |||||||
|  |  | ||||||
|             if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) { |             if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) { | ||||||
|                 // Compile cached file into bytecode cache |                 // Compile cached file into bytecode cache | ||||||
|                 if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) { |                 if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) { | ||||||
|                     @opcache_invalidate($key, true); |                     @opcache_invalidate($key, true); | ||||||
|                 } elseif (\function_exists('apc_compile_file')) { |                 } elseif (\function_exists('apc_compile_file')) { | ||||||
|                     apc_compile_file($key); |                     apc_compile_file($key); | ||||||
|   | |||||||
| @@ -14,11 +14,9 @@ namespace Twig\Cache; | |||||||
| /** | /** | ||||||
|  * Implements a no-cache strategy. |  * Implements a no-cache strategy. | ||||||
|  * |  * | ||||||
|  * @final |  | ||||||
|  * |  | ||||||
|  * @author Fabien Potencier <fabien@symfony.com> |  * @author Fabien Potencier <fabien@symfony.com> | ||||||
|  */ |  */ | ||||||
| class NullCache implements CacheInterface | final class NullCache implements CacheInterface | ||||||
| { | { | ||||||
|     public function generateKey($name, $className) |     public function generateKey($name, $className) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -12,23 +12,22 @@ | |||||||
|  |  | ||||||
| namespace Twig; | namespace Twig; | ||||||
|  |  | ||||||
| use Twig\Node\ModuleNode; | use Twig\Node\Node; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Compiles a node to PHP code. |  * Compiles a node to PHP code. | ||||||
|  * |  * | ||||||
|  * @author Fabien Potencier <fabien@symfony.com> |  * @author Fabien Potencier <fabien@symfony.com> | ||||||
|  */ |  */ | ||||||
| class Compiler implements \Twig_CompilerInterface | class Compiler | ||||||
| { | { | ||||||
|     protected $lastLine; |     private $lastLine; | ||||||
|     protected $source; |     private $source; | ||||||
|     protected $indentation; |     private $indentation; | ||||||
|     protected $env; |     private $env; | ||||||
|     protected $debugInfo = []; |     private $debugInfo = []; | ||||||
|     protected $sourceOffset; |     private $sourceOffset; | ||||||
|     protected $sourceLine; |     private $sourceLine; | ||||||
|     protected $filename; |  | ||||||
|     private $varNameSalt = 0; |     private $varNameSalt = 0; | ||||||
|  |  | ||||||
|     public function __construct(Environment $env) |     public function __construct(Environment $env) | ||||||
| @@ -36,16 +35,6 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|         $this->env = $env; |         $this->env = $env; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @deprecated since 1.25 (to be removed in 2.0) |  | ||||||
|      */ |  | ||||||
|     public function getFilename() |  | ||||||
|     { |  | ||||||
|         @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|         return $this->filename; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the environment instance related to this compiler. |      * Returns the environment instance related to this compiler. | ||||||
|      * |      * | ||||||
| @@ -73,7 +62,7 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|      * |      * | ||||||
|      * @return $this |      * @return $this | ||||||
|      */ |      */ | ||||||
|     public function compile(\Twig_NodeInterface $node, $indentation = 0) |     public function compile(Node $node, $indentation = 0) | ||||||
|     { |     { | ||||||
|         $this->lastLine = null; |         $this->lastLine = null; | ||||||
|         $this->source = ''; |         $this->source = ''; | ||||||
| @@ -84,17 +73,12 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|         $this->indentation = $indentation; |         $this->indentation = $indentation; | ||||||
|         $this->varNameSalt = 0; |         $this->varNameSalt = 0; | ||||||
|  |  | ||||||
|         if ($node instanceof ModuleNode) { |  | ||||||
|             // to be removed in 2.0 |  | ||||||
|             $this->filename = $node->getTemplateName(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $node->compile($this); |         $node->compile($this); | ||||||
|  |  | ||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function subcompile(\Twig_NodeInterface $node, $raw = true) |     public function subcompile(Node $node, $raw = true) | ||||||
|     { |     { | ||||||
|         if (false === $raw) { |         if (false === $raw) { | ||||||
|             $this->source .= str_repeat(' ', $this->indentation * 4); |             $this->source .= str_repeat(' ', $this->indentation * 4); | ||||||
| @@ -124,9 +108,8 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|      * |      * | ||||||
|      * @return $this |      * @return $this | ||||||
|      */ |      */ | ||||||
|     public function write() |     public function write(...$strings) | ||||||
|     { |     { | ||||||
|         $strings = \func_get_args(); |  | ||||||
|         foreach ($strings as $string) { |         foreach ($strings as $string) { | ||||||
|             $this->source .= str_repeat(' ', $this->indentation * 4).$string; |             $this->source .= str_repeat(' ', $this->indentation * 4).$string; | ||||||
|         } |         } | ||||||
| @@ -134,22 +117,6 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Appends an indentation to the current PHP code after compilation. |  | ||||||
|      * |  | ||||||
|      * @return $this |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.27 (to be removed in 2.0). |  | ||||||
|      */ |  | ||||||
|     public function addIndentation() |  | ||||||
|     { |  | ||||||
|         @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|         $this->source .= str_repeat(' ', $this->indentation * 4); |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Adds a quoted string to the compiled code. |      * Adds a quoted string to the compiled code. | ||||||
|      * |      * | ||||||
| @@ -174,21 +141,21 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|     public function repr($value) |     public function repr($value) | ||||||
|     { |     { | ||||||
|         if (\is_int($value) || \is_float($value)) { |         if (\is_int($value) || \is_float($value)) { | ||||||
|             if (false !== $locale = setlocale(LC_NUMERIC, '0')) { |             if (false !== $locale = setlocale(\LC_NUMERIC, '0')) { | ||||||
|                 setlocale(LC_NUMERIC, 'C'); |                 setlocale(\LC_NUMERIC, 'C'); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             $this->raw(var_export($value, true)); |             $this->raw(var_export($value, true)); | ||||||
|  |  | ||||||
|             if (false !== $locale) { |             if (false !== $locale) { | ||||||
|                 setlocale(LC_NUMERIC, $locale); |                 setlocale(\LC_NUMERIC, $locale); | ||||||
|             } |             } | ||||||
|         } elseif (null === $value) { |         } elseif (null === $value) { | ||||||
|             $this->raw('null'); |             $this->raw('null'); | ||||||
|         } elseif (\is_bool($value)) { |         } elseif (\is_bool($value)) { | ||||||
|             $this->raw($value ? 'true' : 'false'); |             $this->raw($value ? 'true' : 'false'); | ||||||
|         } elseif (\is_array($value)) { |         } elseif (\is_array($value)) { | ||||||
|             $this->raw('['); |             $this->raw('array('); | ||||||
|             $first = true; |             $first = true; | ||||||
|             foreach ($value as $key => $v) { |             foreach ($value as $key => $v) { | ||||||
|                 if (!$first) { |                 if (!$first) { | ||||||
| @@ -199,7 +166,7 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|                 $this->raw(' => '); |                 $this->raw(' => '); | ||||||
|                 $this->repr($v); |                 $this->repr($v); | ||||||
|             } |             } | ||||||
|             $this->raw(']'); |             $this->raw(')'); | ||||||
|         } else { |         } else { | ||||||
|             $this->string($value); |             $this->string($value); | ||||||
|         } |         } | ||||||
| @@ -212,22 +179,12 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|      * |      * | ||||||
|      * @return $this |      * @return $this | ||||||
|      */ |      */ | ||||||
|     public function addDebugInfo(\Twig_NodeInterface $node) |     public function addDebugInfo(Node $node) | ||||||
|     { |     { | ||||||
|         if ($node->getTemplateLine() != $this->lastLine) { |         if ($node->getTemplateLine() != $this->lastLine) { | ||||||
|             $this->write(sprintf("// line %d\n", $node->getTemplateLine())); |             $this->write(sprintf("// line %d\n", $node->getTemplateLine())); | ||||||
|  |  | ||||||
|             // when mbstring.func_overload is set to 2 |             $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); | ||||||
|             // mb_substr_count() replaces substr_count() |  | ||||||
|             // but they have different signatures! |  | ||||||
|             if (((int) ini_get('mbstring.func_overload')) & 2) { |  | ||||||
|                 @trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|                 // this is much slower than the "right" version |  | ||||||
|                 $this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n"); |  | ||||||
|             } else { |  | ||||||
|                 $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); |  | ||||||
|             } |  | ||||||
|             $this->sourceOffset = \strlen($this->source); |             $this->sourceOffset = \strlen($this->source); | ||||||
|             $this->debugInfo[$this->sourceLine] = $node->getTemplateLine(); |             $this->debugInfo[$this->sourceLine] = $node->getTemplateLine(); | ||||||
|  |  | ||||||
| @@ -281,7 +238,7 @@ class Compiler implements \Twig_CompilerInterface | |||||||
|  |  | ||||||
|     public function getVarName() |     public function getVarName() | ||||||
|     { |     { | ||||||
|         return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++)); |         return sprintf('__internal_compile_%d', $this->varNameSalt++); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -38,11 +38,9 @@ use Twig\Template; | |||||||
|  */ |  */ | ||||||
| class Error extends \Exception | class Error extends \Exception | ||||||
| { | { | ||||||
|     protected $lineno; |     private $lineno; | ||||||
|     // to be renamed to name in 2.0 |     private $name; | ||||||
|     protected $filename; |     private $rawMessage; | ||||||
|     protected $rawMessage; |  | ||||||
|  |  | ||||||
|     private $sourcePath; |     private $sourcePath; | ||||||
|     private $sourceCode; |     private $sourceCode; | ||||||
|  |  | ||||||
| @@ -57,22 +55,23 @@ class Error extends \Exception | |||||||
|      * @param Source|string|null $source   The source context where the error occurred |      * @param Source|string|null $source   The source context where the error occurred | ||||||
|      * @param \Exception         $previous The previous exception |      * @param \Exception         $previous The previous exception | ||||||
|      */ |      */ | ||||||
|     public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null) |     public function __construct(string $message, int $lineno = -1, $source = null, \Exception $previous = null) | ||||||
|     { |     { | ||||||
|  |         parent::__construct('', 0, $previous); | ||||||
|  |  | ||||||
|         if (null === $source) { |         if (null === $source) { | ||||||
|             $name = null; |             $name = null; | ||||||
|         } elseif (!$source instanceof Source) { |         } elseif (!$source instanceof Source && !$source instanceof \Twig_Source) { | ||||||
|             // for compat with the Twig C ext., passing the template name as string is accepted |             @trigger_error(sprintf('Passing a string as a source to %s is deprecated since Twig 2.6.1; pass a Twig\Source instance instead.', __CLASS__), \E_USER_DEPRECATED); | ||||||
|             $name = $source; |             $name = $source; | ||||||
|         } else { |         } else { | ||||||
|             $name = $source->getName(); |             $name = $source->getName(); | ||||||
|             $this->sourceCode = $source->getCode(); |             $this->sourceCode = $source->getCode(); | ||||||
|             $this->sourcePath = $source->getPath(); |             $this->sourcePath = $source->getPath(); | ||||||
|         } |         } | ||||||
|         parent::__construct('', 0, $previous); |  | ||||||
|  |  | ||||||
|         $this->lineno = $lineno; |         $this->lineno = $lineno; | ||||||
|         $this->filename = $name; |         $this->name = $name; | ||||||
|         $this->rawMessage = $message; |         $this->rawMessage = $message; | ||||||
|         $this->updateRepr(); |         $this->updateRepr(); | ||||||
|     } |     } | ||||||
| @@ -87,67 +86,6 @@ class Error extends \Exception | |||||||
|         return $this->rawMessage; |         return $this->rawMessage; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Gets the logical name where the error occurred. |  | ||||||
|      * |  | ||||||
|      * @return string The name |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead. |  | ||||||
|      */ |  | ||||||
|     public function getTemplateFile() |  | ||||||
|     { |  | ||||||
|         @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|         return $this->filename; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Sets the logical name where the error occurred. |  | ||||||
|      * |  | ||||||
|      * @param string $name The name |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.27 (to be removed in 2.0). Use setSourceContext() instead. |  | ||||||
|      */ |  | ||||||
|     public function setTemplateFile($name) |  | ||||||
|     { |  | ||||||
|         @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|         $this->filename = $name; |  | ||||||
|  |  | ||||||
|         $this->updateRepr(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Gets the logical name where the error occurred. |  | ||||||
|      * |  | ||||||
|      * @return string The name |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.29 (to be removed in 2.0). Use getSourceContext() instead. |  | ||||||
|      */ |  | ||||||
|     public function getTemplateName() |  | ||||||
|     { |  | ||||||
|         @trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|         return $this->filename; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Sets the logical name where the error occurred. |  | ||||||
|      * |  | ||||||
|      * @param string $name The name |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.29 (to be removed in 2.0). Use setSourceContext() instead. |  | ||||||
|      */ |  | ||||||
|     public function setTemplateName($name) |  | ||||||
|     { |  | ||||||
|         @trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|         $this->filename = $name; |  | ||||||
|         $this->sourceCode = $this->sourcePath = null; |  | ||||||
|  |  | ||||||
|         $this->updateRepr(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Gets the template line where the error occurred. |      * Gets the template line where the error occurred. | ||||||
|      * |      * | ||||||
| @@ -177,7 +115,7 @@ class Error extends \Exception | |||||||
|      */ |      */ | ||||||
|     public function getSourceContext() |     public function getSourceContext() | ||||||
|     { |     { | ||||||
|         return $this->filename ? new Source($this->sourceCode, $this->filename, $this->sourcePath) : null; |         return $this->name ? new Source($this->sourceCode, $this->name, $this->sourcePath) : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -186,10 +124,10 @@ class Error extends \Exception | |||||||
|     public function setSourceContext(Source $source = null) |     public function setSourceContext(Source $source = null) | ||||||
|     { |     { | ||||||
|         if (null === $source) { |         if (null === $source) { | ||||||
|             $this->sourceCode = $this->filename = $this->sourcePath = null; |             $this->sourceCode = $this->name = $this->sourcePath = null; | ||||||
|         } else { |         } else { | ||||||
|             $this->sourceCode = $source->getCode(); |             $this->sourceCode = $source->getCode(); | ||||||
|             $this->filename = $source->getName(); |             $this->name = $source->getName(); | ||||||
|             $this->sourcePath = $source->getPath(); |             $this->sourcePath = $source->getPath(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -208,10 +146,7 @@ class Error extends \Exception | |||||||
|         $this->updateRepr(); |         $this->updateRepr(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     private function updateRepr() | ||||||
|      * @internal |  | ||||||
|      */ |  | ||||||
|     protected function updateRepr() |  | ||||||
|     { |     { | ||||||
|         $this->message = $this->rawMessage; |         $this->message = $this->rawMessage; | ||||||
|  |  | ||||||
| @@ -234,11 +169,11 @@ class Error extends \Exception | |||||||
|             $questionMark = true; |             $questionMark = true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($this->filename) { |         if ($this->name) { | ||||||
|             if (\is_string($this->filename) || (\is_object($this->filename) && method_exists($this->filename, '__toString'))) { |             if (\is_string($this->name) || (\is_object($this->name) && method_exists($this->name, '__toString'))) { | ||||||
|                 $name = sprintf('"%s"', $this->filename); |                 $name = sprintf('"%s"', $this->name); | ||||||
|             } else { |             } else { | ||||||
|                 $name = json_encode($this->filename); |                 $name = json_encode($this->name); | ||||||
|             } |             } | ||||||
|             $this->message .= sprintf(' in %s', $name); |             $this->message .= sprintf(' in %s', $name); | ||||||
|         } |         } | ||||||
| @@ -256,20 +191,17 @@ class Error extends \Exception | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     private function guessTemplateInfo() | ||||||
|      * @internal |  | ||||||
|      */ |  | ||||||
|     protected function guessTemplateInfo() |  | ||||||
|     { |     { | ||||||
|         $template = null; |         $template = null; | ||||||
|         $templateClass = null; |         $templateClass = null; | ||||||
|  |  | ||||||
|         $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); |         $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT); | ||||||
|         foreach ($backtrace as $trace) { |         foreach ($backtrace as $trace) { | ||||||
|             if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig_Template' !== \get_class($trace['object'])) { |             if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig\Template' !== \get_class($trace['object'])) { | ||||||
|                 $currentClass = \get_class($trace['object']); |                 $currentClass = \get_class($trace['object']); | ||||||
|                 $isEmbedContainer = 0 === strpos($templateClass, $currentClass); |                 $isEmbedContainer = null === $templateClass ? false : 0 === strpos($templateClass, $currentClass); | ||||||
|                 if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) { |                 if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) { | ||||||
|                     $template = $trace['object']; |                     $template = $trace['object']; | ||||||
|                     $templateClass = \get_class($trace['object']); |                     $templateClass = \get_class($trace['object']); | ||||||
|                 } |                 } | ||||||
| @@ -277,8 +209,8 @@ class Error extends \Exception | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // update template name |         // update template name | ||||||
|         if (null !== $template && null === $this->filename) { |         if (null !== $template && null === $this->name) { | ||||||
|             $this->filename = $template->getTemplateName(); |             $this->name = $template->getTemplateName(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // update template path if any |         // update template path if any | ||||||
| @@ -296,7 +228,7 @@ class Error extends \Exception | |||||||
|         $file = $r->getFileName(); |         $file = $r->getFileName(); | ||||||
|  |  | ||||||
|         $exceptions = [$e = $this]; |         $exceptions = [$e = $this]; | ||||||
|         while ($e instanceof self && $e = $e->getPrevious()) { |         while ($e = $e->getPrevious()) { | ||||||
|             $exceptions[] = $e; |             $exceptions[] = $e; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,20 +26,6 @@ class SyntaxError extends Error | |||||||
|      * @param array  $items An array of possible items |      * @param array  $items An array of possible items | ||||||
|      */ |      */ | ||||||
|     public function addSuggestions($name, array $items) |     public function addSuggestions($name, array $items) | ||||||
|     { |  | ||||||
|         if (!$alternatives = self::computeAlternatives($name, $items)) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives))); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @internal |  | ||||||
|      * |  | ||||||
|      * To be merged with the addSuggestions() method in 2.0. |  | ||||||
|      */ |  | ||||||
|     public static function computeAlternatives($name, $items) |  | ||||||
|     { |     { | ||||||
|         $alternatives = []; |         $alternatives = []; | ||||||
|         foreach ($items as $item) { |         foreach ($items as $item) { | ||||||
| @@ -48,9 +34,14 @@ class SyntaxError extends Error | |||||||
|                 $alternatives[$item] = $lev; |                 $alternatives[$item] = $lev; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (!$alternatives) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         asort($alternatives); |         asort($alternatives); | ||||||
|  |  | ||||||
|         return array_keys($alternatives); |         $this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', array_keys($alternatives)))); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ | |||||||
| namespace Twig; | namespace Twig; | ||||||
|  |  | ||||||
| use Twig\Error\SyntaxError; | use Twig\Error\SyntaxError; | ||||||
|  | use Twig\Node\Expression\AbstractExpression; | ||||||
| use Twig\Node\Expression\ArrayExpression; | use Twig\Node\Expression\ArrayExpression; | ||||||
| use Twig\Node\Expression\ArrowFunctionExpression; | use Twig\Node\Expression\ArrowFunctionExpression; | ||||||
| use Twig\Node\Expression\AssignNameExpression; | use Twig\Node\Expression\AssignNameExpression; | ||||||
| @@ -24,6 +25,7 @@ use Twig\Node\Expression\GetAttrExpression; | |||||||
| use Twig\Node\Expression\MethodCallExpression; | use Twig\Node\Expression\MethodCallExpression; | ||||||
| use Twig\Node\Expression\NameExpression; | use Twig\Node\Expression\NameExpression; | ||||||
| use Twig\Node\Expression\ParentExpression; | use Twig\Node\Expression\ParentExpression; | ||||||
|  | use Twig\Node\Expression\TestExpression; | ||||||
| use Twig\Node\Expression\Unary\NegUnary; | use Twig\Node\Expression\Unary\NegUnary; | ||||||
| use Twig\Node\Expression\Unary\NotUnary; | use Twig\Node\Expression\Unary\NotUnary; | ||||||
| use Twig\Node\Expression\Unary\PosUnary; | use Twig\Node\Expression\Unary\PosUnary; | ||||||
| @@ -43,30 +45,20 @@ use Twig\Node\Node; | |||||||
|  */ |  */ | ||||||
| class ExpressionParser | class ExpressionParser | ||||||
| { | { | ||||||
|     const OPERATOR_LEFT = 1; |     public const OPERATOR_LEFT = 1; | ||||||
|     const OPERATOR_RIGHT = 2; |     public const OPERATOR_RIGHT = 2; | ||||||
|  |  | ||||||
|     protected $parser; |  | ||||||
|     protected $unaryOperators; |  | ||||||
|     protected $binaryOperators; |  | ||||||
|  |  | ||||||
|  |     private $parser; | ||||||
|     private $env; |     private $env; | ||||||
|  |     private $unaryOperators; | ||||||
|  |     private $binaryOperators; | ||||||
|  |  | ||||||
|     public function __construct(Parser $parser, $env = null) |     public function __construct(Parser $parser, Environment $env) | ||||||
|     { |     { | ||||||
|         $this->parser = $parser; |         $this->parser = $parser; | ||||||
|  |         $this->env = $env; | ||||||
|         if ($env instanceof Environment) { |         $this->unaryOperators = $env->getUnaryOperators(); | ||||||
|             $this->env = $env; |         $this->binaryOperators = $env->getBinaryOperators(); | ||||||
|             $this->unaryOperators = $env->getUnaryOperators(); |  | ||||||
|             $this->binaryOperators = $env->getBinaryOperators(); |  | ||||||
|         } else { |  | ||||||
|             @trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|             $this->env = $parser->getEnvironment(); |  | ||||||
|             $this->unaryOperators = func_get_arg(1); |  | ||||||
|             $this->binaryOperators = func_get_arg(2); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function parseExpression($precedence = 0, $allowArrow = false) |     public function parseExpression($precedence = 0, $allowArrow = false) | ||||||
| @@ -86,7 +78,7 @@ class ExpressionParser | |||||||
|             } elseif ('is' === $token->getValue()) { |             } elseif ('is' === $token->getValue()) { | ||||||
|                 $expr = $this->parseTestExpression($expr); |                 $expr = $this->parseTestExpression($expr); | ||||||
|             } elseif (isset($op['callable'])) { |             } elseif (isset($op['callable'])) { | ||||||
|                 $expr = \call_user_func($op['callable'], $this->parser, $expr); |                 $expr = $op['callable']($this->parser, $expr); | ||||||
|             } else { |             } else { | ||||||
|                 $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); |                 $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); | ||||||
|                 $class = $op['class']; |                 $class = $op['class']; | ||||||
| @@ -111,57 +103,57 @@ class ExpressionParser | |||||||
|         $stream = $this->parser->getStream(); |         $stream = $this->parser->getStream(); | ||||||
|  |  | ||||||
|         // short array syntax (one argument, no parentheses)? |         // short array syntax (one argument, no parentheses)? | ||||||
|         if ($stream->look(1)->test(Token::ARROW_TYPE)) { |         if ($stream->look(1)->test(/* Token::ARROW_TYPE */ 12)) { | ||||||
|             $line = $stream->getCurrent()->getLine(); |             $line = $stream->getCurrent()->getLine(); | ||||||
|             $token = $stream->expect(Token::NAME_TYPE); |             $token = $stream->expect(/* Token::NAME_TYPE */ 5); | ||||||
|             $names = [new AssignNameExpression($token->getValue(), $token->getLine())]; |             $names = [new AssignNameExpression($token->getValue(), $token->getLine())]; | ||||||
|             $stream->expect(Token::ARROW_TYPE); |             $stream->expect(/* Token::ARROW_TYPE */ 12); | ||||||
|  |  | ||||||
|             return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); |             return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // first, determine if we are parsing an arrow function by finding => (long form) |         // first, determine if we are parsing an arrow function by finding => (long form) | ||||||
|         $i = 0; |         $i = 0; | ||||||
|         if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) { |         if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|         ++$i; |         ++$i; | ||||||
|         while (true) { |         while (true) { | ||||||
|             // variable name |             // variable name | ||||||
|             ++$i; |             ++$i; | ||||||
|             if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) { |             if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ',')) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             ++$i; |             ++$i; | ||||||
|         } |         } | ||||||
|         if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) { |         if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|         ++$i; |         ++$i; | ||||||
|         if (!$stream->look($i)->test(Token::ARROW_TYPE)) { |         if (!$stream->look($i)->test(/* Token::ARROW_TYPE */ 12)) { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // yes, let's parse it properly |         // yes, let's parse it properly | ||||||
|         $token = $stream->expect(Token::PUNCTUATION_TYPE, '('); |         $token = $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '('); | ||||||
|         $line = $token->getLine(); |         $line = $token->getLine(); | ||||||
|  |  | ||||||
|         $names = []; |         $names = []; | ||||||
|         while (true) { |         while (true) { | ||||||
|             $token = $stream->expect(Token::NAME_TYPE); |             $token = $stream->expect(/* Token::NAME_TYPE */ 5); | ||||||
|             $names[] = new AssignNameExpression($token->getValue(), $token->getLine()); |             $names[] = new AssignNameExpression($token->getValue(), $token->getLine()); | ||||||
|  |  | ||||||
|             if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { |             if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, ')'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')'); | ||||||
|         $stream->expect(Token::ARROW_TYPE); |         $stream->expect(/* Token::ARROW_TYPE */ 12); | ||||||
|  |  | ||||||
|         return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); |         return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function getPrimary() |     private function getPrimary(): AbstractExpression | ||||||
|     { |     { | ||||||
|         $token = $this->parser->getCurrentToken(); |         $token = $this->parser->getCurrentToken(); | ||||||
|  |  | ||||||
| @@ -172,10 +164,10 @@ class ExpressionParser | |||||||
|             $class = $operator['class']; |             $class = $operator['class']; | ||||||
|  |  | ||||||
|             return $this->parsePostfixExpression(new $class($expr, $token->getLine())); |             return $this->parsePostfixExpression(new $class($expr, $token->getLine())); | ||||||
|         } elseif ($token->test(Token::PUNCTUATION_TYPE, '(')) { |         } elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { | ||||||
|             $this->parser->getStream()->next(); |             $this->parser->getStream()->next(); | ||||||
|             $expr = $this->parseExpression(); |             $expr = $this->parseExpression(); | ||||||
|             $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); |             $this->parser->getStream()->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'An opened parenthesis is not properly closed'); | ||||||
|  |  | ||||||
|             return $this->parsePostfixExpression($expr); |             return $this->parsePostfixExpression($expr); | ||||||
|         } |         } | ||||||
| @@ -183,12 +175,12 @@ class ExpressionParser | |||||||
|         return $this->parsePrimaryExpression(); |         return $this->parsePrimaryExpression(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function parseConditionalExpression($expr) |     private function parseConditionalExpression($expr): AbstractExpression | ||||||
|     { |     { | ||||||
|         while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) { |         while ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, '?')) { | ||||||
|             if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) { |             if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { | ||||||
|                 $expr2 = $this->parseExpression(); |                 $expr2 = $this->parseExpression(); | ||||||
|                 if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) { |                 if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { | ||||||
|                     $expr3 = $this->parseExpression(); |                     $expr3 = $this->parseExpression(); | ||||||
|                 } else { |                 } else { | ||||||
|                     $expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine()); |                     $expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine()); | ||||||
| @@ -204,21 +196,21 @@ class ExpressionParser | |||||||
|         return $expr; |         return $expr; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function isUnary(Token $token) |     private function isUnary(Token $token): bool | ||||||
|     { |     { | ||||||
|         return $token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]); |         return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->unaryOperators[$token->getValue()]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function isBinary(Token $token) |     private function isBinary(Token $token): bool | ||||||
|     { |     { | ||||||
|         return $token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]); |         return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->binaryOperators[$token->getValue()]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function parsePrimaryExpression() |     public function parsePrimaryExpression() | ||||||
|     { |     { | ||||||
|         $token = $this->parser->getCurrentToken(); |         $token = $this->parser->getCurrentToken(); | ||||||
|         switch ($token->getType()) { |         switch ($token->getType()) { | ||||||
|             case Token::NAME_TYPE: |             case /* Token::NAME_TYPE */ 5: | ||||||
|                 $this->parser->getStream()->next(); |                 $this->parser->getStream()->next(); | ||||||
|                 switch ($token->getValue()) { |                 switch ($token->getValue()) { | ||||||
|                     case 'true': |                     case 'true': | ||||||
| @@ -247,17 +239,17 @@ class ExpressionParser | |||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case Token::NUMBER_TYPE: |             case /* Token::NUMBER_TYPE */ 6: | ||||||
|                 $this->parser->getStream()->next(); |                 $this->parser->getStream()->next(); | ||||||
|                 $node = new ConstantExpression($token->getValue(), $token->getLine()); |                 $node = new ConstantExpression($token->getValue(), $token->getLine()); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case Token::STRING_TYPE: |             case /* Token::STRING_TYPE */ 7: | ||||||
|             case Token::INTERPOLATION_START_TYPE: |             case /* Token::INTERPOLATION_START_TYPE */ 10: | ||||||
|                 $node = $this->parseStringExpression(); |                 $node = $this->parseStringExpression(); | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case Token::OPERATOR_TYPE: |             case /* Token::OPERATOR_TYPE */ 8: | ||||||
|                 if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) { |                 if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) { | ||||||
|                     // in this context, string operators are variable names |                     // in this context, string operators are variable names | ||||||
|                     $this->parser->getStream()->next(); |                     $this->parser->getStream()->next(); | ||||||
| @@ -267,10 +259,8 @@ class ExpressionParser | |||||||
|                     $class = $this->unaryOperators[$token->getValue()]['class']; |                     $class = $this->unaryOperators[$token->getValue()]['class']; | ||||||
|  |  | ||||||
|                     $ref = new \ReflectionClass($class); |                     $ref = new \ReflectionClass($class); | ||||||
|                     $negClass = 'Twig\Node\Expression\Unary\NegUnary'; |                     if (!(\in_array($ref->getName(), [NegUnary::class, PosUnary::class, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos']) | ||||||
|                     $posClass = 'Twig\Node\Expression\Unary\PosUnary'; |                         || $ref->isSubclassOf(NegUnary::class) || $ref->isSubclassOf(PosUnary::class) | ||||||
|                     if (!(\in_array($ref->getName(), [$negClass, $posClass, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos']) |  | ||||||
|                         || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass) |  | ||||||
|                         || $ref->isSubclassOf('Twig_Node_Expression_Unary_Neg') || $ref->isSubclassOf('Twig_Node_Expression_Unary_Pos')) |                         || $ref->isSubclassOf('Twig_Node_Expression_Unary_Neg') || $ref->isSubclassOf('Twig_Node_Expression_Unary_Pos')) | ||||||
|                     ) { |                     ) { | ||||||
|                         throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); |                         throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); | ||||||
| @@ -285,11 +275,11 @@ class ExpressionParser | |||||||
|  |  | ||||||
|                 // no break |                 // no break | ||||||
|             default: |             default: | ||||||
|                 if ($token->test(Token::PUNCTUATION_TYPE, '[')) { |                 if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) { | ||||||
|                     $node = $this->parseArrayExpression(); |                     $node = $this->parseArrayExpression(); | ||||||
|                 } elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) { |                 } elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) { | ||||||
|                     $node = $this->parseHashExpression(); |                     $node = $this->parseHashExpression(); | ||||||
|                 } elseif ($token->test(Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) { |                 } elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) { | ||||||
|                     throw new SyntaxError(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); |                     throw new SyntaxError(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); | ||||||
|                 } else { |                 } else { | ||||||
|                     throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); |                     throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); | ||||||
| @@ -307,12 +297,12 @@ class ExpressionParser | |||||||
|         // a string cannot be followed by another string in a single expression |         // a string cannot be followed by another string in a single expression | ||||||
|         $nextCanBeString = true; |         $nextCanBeString = true; | ||||||
|         while (true) { |         while (true) { | ||||||
|             if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) { |             if ($nextCanBeString && $token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) { | ||||||
|                 $nodes[] = new ConstantExpression($token->getValue(), $token->getLine()); |                 $nodes[] = new ConstantExpression($token->getValue(), $token->getLine()); | ||||||
|                 $nextCanBeString = false; |                 $nextCanBeString = false; | ||||||
|             } elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) { |             } elseif ($stream->nextIf(/* Token::INTERPOLATION_START_TYPE */ 10)) { | ||||||
|                 $nodes[] = $this->parseExpression(); |                 $nodes[] = $this->parseExpression(); | ||||||
|                 $stream->expect(Token::INTERPOLATION_END_TYPE); |                 $stream->expect(/* Token::INTERPOLATION_END_TYPE */ 11); | ||||||
|                 $nextCanBeString = true; |                 $nextCanBeString = true; | ||||||
|             } else { |             } else { | ||||||
|                 break; |                 break; | ||||||
| @@ -330,16 +320,16 @@ class ExpressionParser | |||||||
|     public function parseArrayExpression() |     public function parseArrayExpression() | ||||||
|     { |     { | ||||||
|         $stream = $this->parser->getStream(); |         $stream = $this->parser->getStream(); | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'An array element was expected'); | ||||||
|  |  | ||||||
|         $node = new ArrayExpression([], $stream->getCurrent()->getLine()); |         $node = new ArrayExpression([], $stream->getCurrent()->getLine()); | ||||||
|         $first = true; |         $first = true; | ||||||
|         while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) { |         while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { | ||||||
|             if (!$first) { |             if (!$first) { | ||||||
|                 $stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); |                 $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'An array element must be followed by a comma'); | ||||||
|  |  | ||||||
|                 // trailing ,? |                 // trailing ,? | ||||||
|                 if ($stream->test(Token::PUNCTUATION_TYPE, ']')) { |                 if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -347,7 +337,7 @@ class ExpressionParser | |||||||
|  |  | ||||||
|             $node->addElement($this->parseExpression()); |             $node->addElement($this->parseExpression()); | ||||||
|         } |         } | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed'); | ||||||
|  |  | ||||||
|         return $node; |         return $node; | ||||||
|     } |     } | ||||||
| @@ -355,16 +345,16 @@ class ExpressionParser | |||||||
|     public function parseHashExpression() |     public function parseHashExpression() | ||||||
|     { |     { | ||||||
|         $stream = $this->parser->getStream(); |         $stream = $this->parser->getStream(); | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '{', 'A hash element was expected'); | ||||||
|  |  | ||||||
|         $node = new ArrayExpression([], $stream->getCurrent()->getLine()); |         $node = new ArrayExpression([], $stream->getCurrent()->getLine()); | ||||||
|         $first = true; |         $first = true; | ||||||
|         while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) { |         while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) { | ||||||
|             if (!$first) { |             if (!$first) { | ||||||
|                 $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma'); |                 $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A hash value must be followed by a comma'); | ||||||
|  |  | ||||||
|                 // trailing ,? |                 // trailing ,? | ||||||
|                 if ($stream->test(Token::PUNCTUATION_TYPE, '}')) { |                 if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) { | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -376,9 +366,18 @@ class ExpressionParser | |||||||
|             //  * a string -- 'a' |             //  * a string -- 'a' | ||||||
|             //  * a name, which is equivalent to a string -- a |             //  * a name, which is equivalent to a string -- a | ||||||
|             //  * an expression, which must be enclosed in parentheses -- (1 + 2) |             //  * an expression, which must be enclosed in parentheses -- (1 + 2) | ||||||
|             if (($token = $stream->nextIf(Token::STRING_TYPE)) || ($token = $stream->nextIf(Token::NAME_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) { |             if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) { | ||||||
|                 $key = new ConstantExpression($token->getValue(), $token->getLine()); |                 $key = new ConstantExpression($token->getValue(), $token->getLine()); | ||||||
|             } elseif ($stream->test(Token::PUNCTUATION_TYPE, '(')) { |  | ||||||
|  |                 // {a} is a shortcut for {a:a} | ||||||
|  |                 if ($stream->test(Token::PUNCTUATION_TYPE, [',', '}'])) { | ||||||
|  |                     $value = new NameExpression($key->getAttribute('value'), $key->getTemplateLine()); | ||||||
|  |                     $node->addElement($value, $key); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } elseif (($token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) || $token = $stream->nextIf(/* Token::NUMBER_TYPE */ 6)) { | ||||||
|  |                 $key = new ConstantExpression($token->getValue(), $token->getLine()); | ||||||
|  |             } elseif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { | ||||||
|                 $key = $this->parseExpression(); |                 $key = $this->parseExpression(); | ||||||
|             } else { |             } else { | ||||||
|                 $current = $stream->getCurrent(); |                 $current = $stream->getCurrent(); | ||||||
| @@ -386,12 +385,12 @@ class ExpressionParser | |||||||
|                 throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext()); |                 throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext()); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             $stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); |             $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ':', 'A hash key must be followed by a colon (:)'); | ||||||
|             $value = $this->parseExpression(); |             $value = $this->parseExpression(); | ||||||
|  |  | ||||||
|             $node->addElement($value, $key); |             $node->addElement($value, $key); | ||||||
|         } |         } | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '}', 'An opened hash is not properly closed'); | ||||||
|  |  | ||||||
|         return $node; |         return $node; | ||||||
|     } |     } | ||||||
| @@ -400,7 +399,7 @@ class ExpressionParser | |||||||
|     { |     { | ||||||
|         while (true) { |         while (true) { | ||||||
|             $token = $this->parser->getCurrentToken(); |             $token = $this->parser->getCurrentToken(); | ||||||
|             if (Token::PUNCTUATION_TYPE == $token->getType()) { |             if (/* Token::PUNCTUATION_TYPE */ 9 == $token->getType()) { | ||||||
|                 if ('.' == $token->getValue() || '[' == $token->getValue()) { |                 if ('.' == $token->getValue() || '[' == $token->getValue()) { | ||||||
|                     $node = $this->parseSubscriptExpression($node); |                     $node = $this->parseSubscriptExpression($node); | ||||||
|                 } elseif ('|' == $token->getValue()) { |                 } elseif ('|' == $token->getValue()) { | ||||||
| @@ -474,22 +473,22 @@ class ExpressionParser | |||||||
|         if ('.' == $token->getValue()) { |         if ('.' == $token->getValue()) { | ||||||
|             $token = $stream->next(); |             $token = $stream->next(); | ||||||
|             if ( |             if ( | ||||||
|                 Token::NAME_TYPE == $token->getType() |                 /* Token::NAME_TYPE */ 5 == $token->getType() | ||||||
|                 || |                 || | ||||||
|                 Token::NUMBER_TYPE == $token->getType() |                 /* Token::NUMBER_TYPE */ 6 == $token->getType() | ||||||
|                 || |                 || | ||||||
|                 (Token::OPERATOR_TYPE == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue())) |                 (/* Token::OPERATOR_TYPE */ 8 == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue())) | ||||||
|             ) { |             ) { | ||||||
|                 $arg = new ConstantExpression($token->getValue(), $lineno); |                 $arg = new ConstantExpression($token->getValue(), $lineno); | ||||||
|  |  | ||||||
|                 if ($stream->test(Token::PUNCTUATION_TYPE, '(')) { |                 if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { | ||||||
|                     $type = Template::METHOD_CALL; |                     $type = Template::METHOD_CALL; | ||||||
|                     foreach ($this->parseArguments() as $n) { |                     foreach ($this->parseArguments() as $n) { | ||||||
|                         $arguments->addElement($n); |                         $arguments->addElement($n); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext()); |                 throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext()); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) { |             if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) { | ||||||
| @@ -499,11 +498,7 @@ class ExpressionParser | |||||||
|  |  | ||||||
|                 $name = $arg->getAttribute('value'); |                 $name = $arg->getAttribute('value'); | ||||||
|  |  | ||||||
|                 if ($this->parser->isReservedMacroName($name)) { |                 $node = new MethodCallExpression($node, 'macro_'.$name, $arguments, $lineno); | ||||||
|                     throw new SyntaxError(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext()); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 $node = new MethodCallExpression($node, 'get'.$name, $arguments, $lineno); |  | ||||||
|                 $node->setAttribute('safe', true); |                 $node->setAttribute('safe', true); | ||||||
|  |  | ||||||
|                 return $node; |                 return $node; | ||||||
| @@ -513,19 +508,19 @@ class ExpressionParser | |||||||
|  |  | ||||||
|             // slice? |             // slice? | ||||||
|             $slice = false; |             $slice = false; | ||||||
|             if ($stream->test(Token::PUNCTUATION_TYPE, ':')) { |             if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ':')) { | ||||||
|                 $slice = true; |                 $slice = true; | ||||||
|                 $arg = new ConstantExpression(0, $token->getLine()); |                 $arg = new ConstantExpression(0, $token->getLine()); | ||||||
|             } else { |             } else { | ||||||
|                 $arg = $this->parseExpression(); |                 $arg = $this->parseExpression(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) { |             if ($stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { | ||||||
|                 $slice = true; |                 $slice = true; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ($slice) { |             if ($slice) { | ||||||
|                 if ($stream->test(Token::PUNCTUATION_TYPE, ']')) { |                 if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { | ||||||
|                     $length = new ConstantExpression(null, $token->getLine()); |                     $length = new ConstantExpression(null, $token->getLine()); | ||||||
|                 } else { |                 } else { | ||||||
|                     $length = $this->parseExpression(); |                     $length = $this->parseExpression(); | ||||||
| @@ -535,12 +530,12 @@ class ExpressionParser | |||||||
|                 $arguments = new Node([$arg, $length]); |                 $arguments = new Node([$arg, $length]); | ||||||
|                 $filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine()); |                 $filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine()); | ||||||
|  |  | ||||||
|                 $stream->expect(Token::PUNCTUATION_TYPE, ']'); |                 $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']'); | ||||||
|  |  | ||||||
|                 return $filter; |                 return $filter; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             $stream->expect(Token::PUNCTUATION_TYPE, ']'); |             $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']'); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return new GetAttrExpression($node, $arg, $arguments, $type, $lineno); |         return new GetAttrExpression($node, $arg, $arguments, $type, $lineno); | ||||||
| @@ -556,10 +551,10 @@ class ExpressionParser | |||||||
|     public function parseFilterExpressionRaw($node, $tag = null) |     public function parseFilterExpressionRaw($node, $tag = null) | ||||||
|     { |     { | ||||||
|         while (true) { |         while (true) { | ||||||
|             $token = $this->parser->getStream()->expect(Token::NAME_TYPE); |             $token = $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5); | ||||||
|  |  | ||||||
|             $name = new ConstantExpression($token->getValue(), $token->getLine()); |             $name = new ConstantExpression($token->getValue(), $token->getLine()); | ||||||
|             if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) { |             if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { | ||||||
|                 $arguments = new Node(); |                 $arguments = new Node(); | ||||||
|             } else { |             } else { | ||||||
|                 $arguments = $this->parseArguments(true, false, true); |                 $arguments = $this->parseArguments(true, false, true); | ||||||
| @@ -569,7 +564,7 @@ class ExpressionParser | |||||||
|  |  | ||||||
|             $node = new $class($node, $name, $arguments, $token->getLine(), $tag); |             $node = new $class($node, $name, $arguments, $token->getLine(), $tag); | ||||||
|  |  | ||||||
|             if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '|')) { |             if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '|')) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -594,21 +589,26 @@ class ExpressionParser | |||||||
|         $args = []; |         $args = []; | ||||||
|         $stream = $this->parser->getStream(); |         $stream = $this->parser->getStream(); | ||||||
|  |  | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(', 'A list of arguments must begin with an opening parenthesis'); | ||||||
|         while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) { |         while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { | ||||||
|             if (!empty($args)) { |             if (!empty($args)) { | ||||||
|                 $stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); |                 $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'Arguments must be separated by a comma'); | ||||||
|  |  | ||||||
|  |                 // if the comma above was a trailing comma, early exit the argument parse loop | ||||||
|  |                 if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ($definition) { |             if ($definition) { | ||||||
|                 $token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name'); |                 $token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name'); | ||||||
|                 $value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine()); |                 $value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine()); | ||||||
|             } else { |             } else { | ||||||
|                 $value = $this->parseExpression(0, $allowArrow); |                 $value = $this->parseExpression(0, $allowArrow); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             $name = null; |             $name = null; | ||||||
|             if ($namedArguments && $token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) { |             if ($namedArguments && $token = $stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) { | ||||||
|                 if (!$value instanceof NameExpression) { |                 if (!$value instanceof NameExpression) { | ||||||
|                     throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext()); |                     throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext()); | ||||||
|                 } |                 } | ||||||
| @@ -618,7 +618,7 @@ class ExpressionParser | |||||||
|                     $value = $this->parsePrimaryExpression(); |                     $value = $this->parsePrimaryExpression(); | ||||||
|  |  | ||||||
|                     if (!$this->checkConstantExpression($value)) { |                     if (!$this->checkConstantExpression($value)) { | ||||||
|                         throw new SyntaxError(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext()); |                         throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $stream->getSourceContext()); | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     $value = $this->parseExpression(0, $allowArrow); |                     $value = $this->parseExpression(0, $allowArrow); | ||||||
| @@ -639,7 +639,7 @@ class ExpressionParser | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); |         $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'A list of arguments must be closed by a parenthesis'); | ||||||
|  |  | ||||||
|         return new Node($args); |         return new Node($args); | ||||||
|     } |     } | ||||||
| @@ -650,19 +650,19 @@ class ExpressionParser | |||||||
|         $targets = []; |         $targets = []; | ||||||
|         while (true) { |         while (true) { | ||||||
|             $token = $this->parser->getCurrentToken(); |             $token = $this->parser->getCurrentToken(); | ||||||
|             if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) { |             if ($stream->test(/* Token::OPERATOR_TYPE */ 8) && preg_match(Lexer::REGEX_NAME, $token->getValue())) { | ||||||
|                 // in this context, string operators are variable names |                 // in this context, string operators are variable names | ||||||
|                 $this->parser->getStream()->next(); |                 $this->parser->getStream()->next(); | ||||||
|             } else { |             } else { | ||||||
|                 $stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to'); |                 $stream->expect(/* Token::NAME_TYPE */ 5, null, 'Only variables can be assigned to'); | ||||||
|             } |             } | ||||||
|             $value = $token->getValue(); |             $value = $token->getValue(); | ||||||
|             if (\in_array(strtolower($value), ['true', 'false', 'none', 'null'])) { |             if (\in_array(strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), ['true', 'false', 'none', 'null'])) { | ||||||
|                 throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext()); |                 throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext()); | ||||||
|             } |             } | ||||||
|             $targets[] = new AssignNameExpression($value, $token->getLine()); |             $targets[] = new AssignNameExpression($value, $token->getLine()); | ||||||
|  |  | ||||||
|             if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { |             if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -675,7 +675,7 @@ class ExpressionParser | |||||||
|         $targets = []; |         $targets = []; | ||||||
|         while (true) { |         while (true) { | ||||||
|             $targets[] = $this->parseExpression(); |             $targets[] = $this->parseExpression(); | ||||||
|             if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) { |             if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -683,35 +683,42 @@ class ExpressionParser | |||||||
|         return new Node($targets); |         return new Node($targets); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private function parseNotTestExpression(\Twig_NodeInterface $node) |     private function parseNotTestExpression(Node $node): NotUnary | ||||||
|     { |     { | ||||||
|         return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine()); |         return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private function parseTestExpression(\Twig_NodeInterface $node) |     private function parseTestExpression(Node $node): TestExpression | ||||||
|     { |     { | ||||||
|         $stream = $this->parser->getStream(); |         $stream = $this->parser->getStream(); | ||||||
|         list($name, $test) = $this->getTest($node->getTemplateLine()); |         list($name, $test) = $this->getTest($node->getTemplateLine()); | ||||||
|  |  | ||||||
|         $class = $this->getTestNodeClass($test); |         $class = $this->getTestNodeClass($test); | ||||||
|         $arguments = null; |         $arguments = null; | ||||||
|         if ($stream->test(Token::PUNCTUATION_TYPE, '(')) { |         if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { | ||||||
|             $arguments = $this->parseArguments(true); |             $arguments = $this->parseArguments(true); | ||||||
|  |         } elseif ($test->hasOneMandatoryArgument()) { | ||||||
|  |             $arguments = new Node([0 => $this->parsePrimaryExpression()]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) { | ||||||
|  |             $node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine()); | ||||||
|  |             $node->setAttribute('safe', true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine()); |         return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private function getTest($line) |     private function getTest(int $line): array | ||||||
|     { |     { | ||||||
|         $stream = $this->parser->getStream(); |         $stream = $this->parser->getStream(); | ||||||
|         $name = $stream->expect(Token::NAME_TYPE)->getValue(); |         $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); | ||||||
|  |  | ||||||
|         if ($test = $this->env->getTest($name)) { |         if ($test = $this->env->getTest($name)) { | ||||||
|             return [$name, $test]; |             return [$name, $test]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($stream->test(Token::NAME_TYPE)) { |         if ($stream->test(/* Token::NAME_TYPE */ 5)) { | ||||||
|             // try 2-words tests |             // try 2-words tests | ||||||
|             $name = $name.' '.$this->parser->getCurrentToken()->getValue(); |             $name = $name.' '.$this->parser->getCurrentToken()->getValue(); | ||||||
|  |  | ||||||
| @@ -728,11 +735,12 @@ class ExpressionParser | |||||||
|         throw $e; |         throw $e; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private function getTestNodeClass($test) |     private function getTestNodeClass(TwigTest $test): string | ||||||
|     { |     { | ||||||
|         if ($test instanceof TwigTest && $test->isDeprecated()) { |         if ($test->isDeprecated()) { | ||||||
|             $stream = $this->parser->getStream(); |             $stream = $this->parser->getStream(); | ||||||
|             $message = sprintf('Twig Test "%s" is deprecated', $test->getName()); |             $message = sprintf('Twig Test "%s" is deprecated', $test->getName()); | ||||||
|  |  | ||||||
|             if (!\is_bool($test->getDeprecatedVersion())) { |             if (!\is_bool($test->getDeprecatedVersion())) { | ||||||
|                 $message .= sprintf(' since version %s', $test->getDeprecatedVersion()); |                 $message .= sprintf(' since version %s', $test->getDeprecatedVersion()); | ||||||
|             } |             } | ||||||
| @@ -740,19 +748,15 @@ class ExpressionParser | |||||||
|                 $message .= sprintf('. Use "%s" instead', $test->getAlternative()); |                 $message .= sprintf('. Use "%s" instead', $test->getAlternative()); | ||||||
|             } |             } | ||||||
|             $src = $stream->getSourceContext(); |             $src = $stream->getSourceContext(); | ||||||
|             $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine()); |             $message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine()); | ||||||
|  |  | ||||||
|             @trigger_error($message, E_USER_DEPRECATED); |             @trigger_error($message, \E_USER_DEPRECATED); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($test instanceof TwigTest) { |         return $test->getNodeClass(); | ||||||
|             return $test->getNodeClass(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $test instanceof \Twig_Test_Node ? $test->getClass() : 'Twig\Node\Expression\TestExpression'; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function getFunctionNodeClass($name, $line) |     private function getFunctionNodeClass(string $name, int $line): string | ||||||
|     { |     { | ||||||
|         if (false === $function = $this->env->getFunction($name)) { |         if (false === $function = $this->env->getFunction($name)) { | ||||||
|             $e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()); |             $e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()); | ||||||
| @@ -761,7 +765,7 @@ class ExpressionParser | |||||||
|             throw $e; |             throw $e; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($function instanceof TwigFunction && $function->isDeprecated()) { |         if ($function->isDeprecated()) { | ||||||
|             $message = sprintf('Twig Function "%s" is deprecated', $function->getName()); |             $message = sprintf('Twig Function "%s" is deprecated', $function->getName()); | ||||||
|             if (!\is_bool($function->getDeprecatedVersion())) { |             if (!\is_bool($function->getDeprecatedVersion())) { | ||||||
|                 $message .= sprintf(' since version %s', $function->getDeprecatedVersion()); |                 $message .= sprintf(' since version %s', $function->getDeprecatedVersion()); | ||||||
| @@ -770,19 +774,15 @@ class ExpressionParser | |||||||
|                 $message .= sprintf('. Use "%s" instead', $function->getAlternative()); |                 $message .= sprintf('. Use "%s" instead', $function->getAlternative()); | ||||||
|             } |             } | ||||||
|             $src = $this->parser->getStream()->getSourceContext(); |             $src = $this->parser->getStream()->getSourceContext(); | ||||||
|             $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line); |             $message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line); | ||||||
|  |  | ||||||
|             @trigger_error($message, E_USER_DEPRECATED); |             @trigger_error($message, \E_USER_DEPRECATED); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($function instanceof TwigFunction) { |         return $function->getNodeClass(); | ||||||
|             return $function->getNodeClass(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $function instanceof \Twig_Function_Node ? $function->getClass() : 'Twig\Node\Expression\FunctionExpression'; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function getFilterNodeClass($name, $line) |     private function getFilterNodeClass(string $name, int $line): string | ||||||
|     { |     { | ||||||
|         if (false === $filter = $this->env->getFilter($name)) { |         if (false === $filter = $this->env->getFilter($name)) { | ||||||
|             $e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()); |             $e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()); | ||||||
| @@ -791,7 +791,7 @@ class ExpressionParser | |||||||
|             throw $e; |             throw $e; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($filter instanceof TwigFilter && $filter->isDeprecated()) { |         if ($filter->isDeprecated()) { | ||||||
|             $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName()); |             $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName()); | ||||||
|             if (!\is_bool($filter->getDeprecatedVersion())) { |             if (!\is_bool($filter->getDeprecatedVersion())) { | ||||||
|                 $message .= sprintf(' since version %s', $filter->getDeprecatedVersion()); |                 $message .= sprintf(' since version %s', $filter->getDeprecatedVersion()); | ||||||
| @@ -800,20 +800,16 @@ class ExpressionParser | |||||||
|                 $message .= sprintf('. Use "%s" instead', $filter->getAlternative()); |                 $message .= sprintf('. Use "%s" instead', $filter->getAlternative()); | ||||||
|             } |             } | ||||||
|             $src = $this->parser->getStream()->getSourceContext(); |             $src = $this->parser->getStream()->getSourceContext(); | ||||||
|             $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line); |             $message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line); | ||||||
|  |  | ||||||
|             @trigger_error($message, E_USER_DEPRECATED); |             @trigger_error($message, \E_USER_DEPRECATED); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($filter instanceof TwigFilter) { |         return $filter->getNodeClass(); | ||||||
|             return $filter->getNodeClass(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return $filter instanceof \Twig_Filter_Node ? $filter->getClass() : 'Twig\Node\Expression\FilterExpression'; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // checks that the node only contains "constant" elements |     // checks that the node only contains "constant" elements | ||||||
|     protected function checkConstantExpression(\Twig_NodeInterface $node) |     private function checkConstantExpression(Node $node): bool | ||||||
|     { |     { | ||||||
|         if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression |         if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression | ||||||
|             || $node instanceof NegUnary || $node instanceof PosUnary |             || $node instanceof NegUnary || $node instanceof PosUnary | ||||||
|   | |||||||
| @@ -11,17 +11,8 @@ | |||||||
|  |  | ||||||
| namespace Twig\Extension; | namespace Twig\Extension; | ||||||
|  |  | ||||||
| use Twig\Environment; |  | ||||||
|  |  | ||||||
| abstract class AbstractExtension implements ExtensionInterface | abstract class AbstractExtension implements ExtensionInterface | ||||||
| { | { | ||||||
|     /** |  | ||||||
|      * @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead |  | ||||||
|      */ |  | ||||||
|     public function initRuntime(Environment $environment) |  | ||||||
|     { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getTokenParsers() |     public function getTokenParsers() | ||||||
|     { |     { | ||||||
|         return []; |         return []; | ||||||
| @@ -51,22 +42,6 @@ abstract class AbstractExtension implements ExtensionInterface | |||||||
|     { |     { | ||||||
|         return []; |         return []; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead |  | ||||||
|      */ |  | ||||||
|     public function getGlobals() |  | ||||||
|     { |  | ||||||
|         return []; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally |  | ||||||
|      */ |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return \get_class($this); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\AbstractExtension', 'Twig_Extension'); | class_alias('Twig\Extension\AbstractExtension', 'Twig_Extension'); | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -12,10 +12,7 @@ | |||||||
| namespace Twig\Extension { | namespace Twig\Extension { | ||||||
| use Twig\TwigFunction; | use Twig\TwigFunction; | ||||||
|  |  | ||||||
| /** | final class DebugExtension extends AbstractExtension | ||||||
|  * @final |  | ||||||
|  */ |  | ||||||
| class DebugExtension extends AbstractExtension |  | ||||||
| { | { | ||||||
|     public function getFunctions() |     public function getFunctions() | ||||||
|     { |     { | ||||||
| @@ -33,11 +30,6 @@ class DebugExtension extends AbstractExtension | |||||||
|             new TwigFunction('dump', 'twig_var_dump', ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]), |             new TwigFunction('dump', 'twig_var_dump', ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]), | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return 'debug'; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\DebugExtension', 'Twig_Extension_Debug'); | class_alias('Twig\Extension\DebugExtension', 'Twig_Extension_Debug'); | ||||||
| @@ -48,7 +40,7 @@ use Twig\Environment; | |||||||
| use Twig\Template; | use Twig\Template; | ||||||
| use Twig\TemplateWrapper; | use Twig\TemplateWrapper; | ||||||
|  |  | ||||||
| function twig_var_dump(Environment $env, $context, array $vars = []) | function twig_var_dump(Environment $env, $context, ...$vars) | ||||||
| { | { | ||||||
|     if (!$env->isDebug()) { |     if (!$env->isDebug()) { | ||||||
|         return; |         return; | ||||||
| @@ -66,9 +58,7 @@ function twig_var_dump(Environment $env, $context, array $vars = []) | |||||||
|  |  | ||||||
|         var_dump($vars); |         var_dump($vars); | ||||||
|     } else { |     } else { | ||||||
|         foreach ($vars as $var) { |         var_dump(...$vars); | ||||||
|             var_dump($var); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ob_get_clean(); |     return ob_get_clean(); | ||||||
|   | |||||||
| @@ -10,16 +10,21 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| namespace Twig\Extension { | namespace Twig\Extension { | ||||||
|  | use Twig\FileExtensionEscapingStrategy; | ||||||
| use Twig\NodeVisitor\EscaperNodeVisitor; | use Twig\NodeVisitor\EscaperNodeVisitor; | ||||||
| use Twig\TokenParser\AutoEscapeTokenParser; | use Twig\TokenParser\AutoEscapeTokenParser; | ||||||
| use Twig\TwigFilter; | use Twig\TwigFilter; | ||||||
|  |  | ||||||
| /** | final class EscaperExtension extends AbstractExtension | ||||||
|  * @final |  | ||||||
|  */ |  | ||||||
| class EscaperExtension extends AbstractExtension |  | ||||||
| { | { | ||||||
|     protected $defaultStrategy; |     private $defaultStrategy; | ||||||
|  |     private $escapers = []; | ||||||
|  |  | ||||||
|  |     /** @internal */ | ||||||
|  |     public $safeClasses = []; | ||||||
|  |  | ||||||
|  |     /** @internal */ | ||||||
|  |     public $safeLookup = []; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @param string|false|callable $defaultStrategy An escaping strategy |      * @param string|false|callable $defaultStrategy An escaping strategy | ||||||
| @@ -44,6 +49,8 @@ class EscaperExtension extends AbstractExtension | |||||||
|     public function getFilters() |     public function getFilters() | ||||||
|     { |     { | ||||||
|         return [ |         return [ | ||||||
|  |             new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), | ||||||
|  |             new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), | ||||||
|             new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]), |             new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]), | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| @@ -58,21 +65,8 @@ class EscaperExtension extends AbstractExtension | |||||||
|      */ |      */ | ||||||
|     public function setDefaultStrategy($defaultStrategy) |     public function setDefaultStrategy($defaultStrategy) | ||||||
|     { |     { | ||||||
|         // for BC |  | ||||||
|         if (true === $defaultStrategy) { |  | ||||||
|             @trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|             $defaultStrategy = 'html'; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if ('filename' === $defaultStrategy) { |  | ||||||
|             @trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED); |  | ||||||
|  |  | ||||||
|             $defaultStrategy = 'name'; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if ('name' === $defaultStrategy) { |         if ('name' === $defaultStrategy) { | ||||||
|             $defaultStrategy = ['\Twig\FileExtensionEscapingStrategy', 'guess']; |             $defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess']; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->defaultStrategy = $defaultStrategy; |         $this->defaultStrategy = $defaultStrategy; | ||||||
| @@ -96,9 +90,47 @@ class EscaperExtension extends AbstractExtension | |||||||
|         return $this->defaultStrategy; |         return $this->defaultStrategy; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getName() |     /** | ||||||
|  |      * Defines a new escaper to be used via the escape filter. | ||||||
|  |      * | ||||||
|  |      * @param string   $strategy The strategy name that should be used as a strategy in the escape call | ||||||
|  |      * @param callable $callable A valid PHP callable | ||||||
|  |      */ | ||||||
|  |     public function setEscaper($strategy, callable $callable) | ||||||
|     { |     { | ||||||
|         return 'escaper'; |         $this->escapers[$strategy] = $callable; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets all defined escapers. | ||||||
|  |      * | ||||||
|  |      * @return callable[] An array of escapers | ||||||
|  |      */ | ||||||
|  |     public function getEscapers() | ||||||
|  |     { | ||||||
|  |         return $this->escapers; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function setSafeClasses(array $safeClasses = []) | ||||||
|  |     { | ||||||
|  |         $this->safeClasses = []; | ||||||
|  |         $this->safeLookup = []; | ||||||
|  |         foreach ($safeClasses as $class => $strategies) { | ||||||
|  |             $this->addSafeClass($class, $strategies); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addSafeClass(string $class, array $strategies) | ||||||
|  |     { | ||||||
|  |         $class = ltrim($class, '\\'); | ||||||
|  |         if (!isset($this->safeClasses[$class])) { | ||||||
|  |             $this->safeClasses[$class] = []; | ||||||
|  |         } | ||||||
|  |         $this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies); | ||||||
|  |  | ||||||
|  |         foreach ($strategies as $strategy) { | ||||||
|  |             $this->safeLookup[$strategy][$class] = true; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -106,6 +138,14 @@ class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper'); | |||||||
| } | } | ||||||
|  |  | ||||||
| namespace { | namespace { | ||||||
|  | use Twig\Environment; | ||||||
|  | use Twig\Error\RuntimeError; | ||||||
|  | use Twig\Extension\CoreExtension; | ||||||
|  | use Twig\Extension\EscaperExtension; | ||||||
|  | use Twig\Markup; | ||||||
|  | use Twig\Node\Expression\ConstantExpression; | ||||||
|  | use Twig\Node\Node; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Marks a variable as being safe. |  * Marks a variable as being safe. | ||||||
|  * |  * | ||||||
| @@ -117,4 +157,272 @@ function twig_raw_filter($string) | |||||||
| { | { | ||||||
|     return $string; |     return $string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Escapes a string. | ||||||
|  |  * | ||||||
|  |  * @param mixed  $string     The value to be escaped | ||||||
|  |  * @param string $strategy   The escaping strategy | ||||||
|  |  * @param string $charset    The charset | ||||||
|  |  * @param bool   $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) | ||||||
|  |  * | ||||||
|  |  * @return string | ||||||
|  |  */ | ||||||
|  | function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) | ||||||
|  | { | ||||||
|  |     if ($autoescape && $string instanceof Markup) { | ||||||
|  |         return $string; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!\is_string($string)) { | ||||||
|  |         if (\is_object($string) && method_exists($string, '__toString')) { | ||||||
|  |             if ($autoescape) { | ||||||
|  |                 $c = \get_class($string); | ||||||
|  |                 $ext = $env->getExtension(EscaperExtension::class); | ||||||
|  |                 if (!isset($ext->safeClasses[$c])) { | ||||||
|  |                     $ext->safeClasses[$c] = []; | ||||||
|  |                     foreach (class_parents($string) + class_implements($string) as $class) { | ||||||
|  |                         if (isset($ext->safeClasses[$class])) { | ||||||
|  |                             $ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class])); | ||||||
|  |                             foreach ($ext->safeClasses[$class] as $s) { | ||||||
|  |                                 $ext->safeLookup[$s][$c] = true; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) { | ||||||
|  |                     return (string) $string; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $string = (string) $string; | ||||||
|  |         } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) { | ||||||
|  |             return $string; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ('' === $string) { | ||||||
|  |         return ''; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (null === $charset) { | ||||||
|  |         $charset = $env->getCharset(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     switch ($strategy) { | ||||||
|  |         case 'html': | ||||||
|  |             // see https://www.php.net/htmlspecialchars | ||||||
|  |  | ||||||
|  |             // Using a static variable to avoid initializing the array | ||||||
|  |             // each time the function is called. Moving the declaration on the | ||||||
|  |             // top of the function slow downs other escaping strategies. | ||||||
|  |             static $htmlspecialcharsCharsets = [ | ||||||
|  |                 'ISO-8859-1' => true, 'ISO8859-1' => true, | ||||||
|  |                 'ISO-8859-15' => true, 'ISO8859-15' => true, | ||||||
|  |                 'utf-8' => true, 'UTF-8' => true, | ||||||
|  |                 'CP866' => true, 'IBM866' => true, '866' => true, | ||||||
|  |                 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, | ||||||
|  |                 '1251' => true, | ||||||
|  |                 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, | ||||||
|  |                 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, | ||||||
|  |                 'BIG5' => true, '950' => true, | ||||||
|  |                 'GB2312' => true, '936' => true, | ||||||
|  |                 'BIG5-HKSCS' => true, | ||||||
|  |                 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, | ||||||
|  |                 'EUC-JP' => true, 'EUCJP' => true, | ||||||
|  |                 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, | ||||||
|  |             ]; | ||||||
|  |  | ||||||
|  |             if (isset($htmlspecialcharsCharsets[$charset])) { | ||||||
|  |                 return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { | ||||||
|  |                 // cache the lowercase variant for future iterations | ||||||
|  |                 $htmlspecialcharsCharsets[$charset] = true; | ||||||
|  |  | ||||||
|  |                 return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||||||
|  |             $string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'); | ||||||
|  |  | ||||||
|  |             return iconv('UTF-8', $charset, $string); | ||||||
|  |  | ||||||
|  |         case 'js': | ||||||
|  |             // escape all non-alphanumeric characters | ||||||
|  |             // into their \x or \uHHHH representations | ||||||
|  |             if ('UTF-8' !== $charset) { | ||||||
|  |                 $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!preg_match('//u', $string)) { | ||||||
|  |                 throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) { | ||||||
|  |                 $char = $matches[0]; | ||||||
|  |  | ||||||
|  |                 /* | ||||||
|  |                  * A few characters have short escape sequences in JSON and JavaScript. | ||||||
|  |                  * Escape sequences supported only by JavaScript, not JSON, are omitted. | ||||||
|  |                  * \" is also supported but omitted, because the resulting string is not HTML safe. | ||||||
|  |                  */ | ||||||
|  |                 static $shortMap = [ | ||||||
|  |                     '\\' => '\\\\', | ||||||
|  |                     '/' => '\\/', | ||||||
|  |                     "\x08" => '\b', | ||||||
|  |                     "\x0C" => '\f', | ||||||
|  |                     "\x0A" => '\n', | ||||||
|  |                     "\x0D" => '\r', | ||||||
|  |                     "\x09" => '\t', | ||||||
|  |                 ]; | ||||||
|  |  | ||||||
|  |                 if (isset($shortMap[$char])) { | ||||||
|  |                     return $shortMap[$char]; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 $codepoint = mb_ord($char, 'UTF-8'); | ||||||
|  |                 if (0x10000 > $codepoint) { | ||||||
|  |                     return sprintf('\u%04X', $codepoint); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Split characters outside the BMP into surrogate pairs | ||||||
|  |                 // https://tools.ietf.org/html/rfc2781.html#section-2.1 | ||||||
|  |                 $u = $codepoint - 0x10000; | ||||||
|  |                 $high = 0xD800 | ($u >> 10); | ||||||
|  |                 $low = 0xDC00 | ($u & 0x3FF); | ||||||
|  |  | ||||||
|  |                 return sprintf('\u%04X\u%04X', $high, $low); | ||||||
|  |             }, $string); | ||||||
|  |  | ||||||
|  |             if ('UTF-8' !== $charset) { | ||||||
|  |                 $string = iconv('UTF-8', $charset, $string); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return $string; | ||||||
|  |  | ||||||
|  |         case 'css': | ||||||
|  |             if ('UTF-8' !== $charset) { | ||||||
|  |                 $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!preg_match('//u', $string)) { | ||||||
|  |                 throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) { | ||||||
|  |                 $char = $matches[0]; | ||||||
|  |  | ||||||
|  |                 return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8')); | ||||||
|  |             }, $string); | ||||||
|  |  | ||||||
|  |             if ('UTF-8' !== $charset) { | ||||||
|  |                 $string = iconv('UTF-8', $charset, $string); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return $string; | ||||||
|  |  | ||||||
|  |         case 'html_attr': | ||||||
|  |             if ('UTF-8' !== $charset) { | ||||||
|  |                 $string = twig_convert_encoding($string, 'UTF-8', $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (!preg_match('//u', $string)) { | ||||||
|  |                 throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) { | ||||||
|  |                 /** | ||||||
|  |                  * This function is adapted from code coming from Zend Framework. | ||||||
|  |                  * | ||||||
|  |                  * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) | ||||||
|  |                  * @license   https://framework.zend.com/license/new-bsd New BSD License | ||||||
|  |                  */ | ||||||
|  |                 $chr = $matches[0]; | ||||||
|  |                 $ord = \ord($chr); | ||||||
|  |  | ||||||
|  |                 /* | ||||||
|  |                  * The following replaces characters undefined in HTML with the | ||||||
|  |                  * hex entity for the Unicode replacement character. | ||||||
|  |                  */ | ||||||
|  |                 if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) { | ||||||
|  |                     return '�'; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 /* | ||||||
|  |                  * Check if the current character to escape has a name entity we should | ||||||
|  |                  * replace it with while grabbing the hex value of the character. | ||||||
|  |                  */ | ||||||
|  |                 if (1 === \strlen($chr)) { | ||||||
|  |                     /* | ||||||
|  |                      * While HTML supports far more named entities, the lowest common denominator | ||||||
|  |                      * has become HTML5's XML Serialisation which is restricted to the those named | ||||||
|  |                      * entities that XML supports. Using HTML entities would result in this error: | ||||||
|  |                      *     XML Parsing Error: undefined entity | ||||||
|  |                      */ | ||||||
|  |                     static $entityMap = [ | ||||||
|  |                         34 => '"', /* quotation mark */ | ||||||
|  |                         38 => '&',  /* ampersand */ | ||||||
|  |                         60 => '<',   /* less-than sign */ | ||||||
|  |                         62 => '>',   /* greater-than sign */ | ||||||
|  |                     ]; | ||||||
|  |  | ||||||
|  |                     if (isset($entityMap[$ord])) { | ||||||
|  |                         return $entityMap[$ord]; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     return sprintf('&#x%02X;', $ord); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 /* | ||||||
|  |                  * Per OWASP recommendations, we'll use hex entities for any other | ||||||
|  |                  * characters where a named entity does not exist. | ||||||
|  |                  */ | ||||||
|  |                 return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8')); | ||||||
|  |             }, $string); | ||||||
|  |  | ||||||
|  |             if ('UTF-8' !== $charset) { | ||||||
|  |                 $string = iconv('UTF-8', $charset, $string); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return $string; | ||||||
|  |  | ||||||
|  |         case 'url': | ||||||
|  |             return rawurlencode($string); | ||||||
|  |  | ||||||
|  |         default: | ||||||
|  |             // check the ones set on CoreExtension for BC (to be removed in 3.0) | ||||||
|  |             $legacyEscapers = $env->getExtension(CoreExtension::class)->getEscapers(false); | ||||||
|  |             if (array_key_exists($strategy, $legacyEscapers)) { | ||||||
|  |                 return $legacyEscapers[$strategy]($env, $string, $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $escapers = $env->getExtension(EscaperExtension::class)->getEscapers(); | ||||||
|  |             if (array_key_exists($strategy, $escapers)) { | ||||||
|  |                 return $escapers[$strategy]($env, $string, $charset); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $escapers = array_merge($legacyEscapers, $escapers); | ||||||
|  |             $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers))); | ||||||
|  |  | ||||||
|  |             throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @internal | ||||||
|  |  */ | ||||||
|  | function twig_escape_filter_is_safe(Node $filterArgs) | ||||||
|  | { | ||||||
|  |     foreach ($filterArgs as $arg) { | ||||||
|  |         if ($arg instanceof ConstantExpression) { | ||||||
|  |             return [$arg->getAttribute('value')]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return []; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ['html']; | ||||||
|  | } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ | |||||||
|  |  | ||||||
| namespace Twig\Extension; | namespace Twig\Extension; | ||||||
|  |  | ||||||
| use Twig\Environment; |  | ||||||
| use Twig\NodeVisitor\NodeVisitorInterface; | use Twig\NodeVisitor\NodeVisitorInterface; | ||||||
| use Twig\TokenParser\TokenParserInterface; | use Twig\TokenParser\TokenParserInterface; | ||||||
| use Twig\TwigFilter; | use Twig\TwigFilter; | ||||||
| @@ -25,15 +24,6 @@ use Twig\TwigTest; | |||||||
|  */ |  */ | ||||||
| interface ExtensionInterface | interface ExtensionInterface | ||||||
| { | { | ||||||
|     /** |  | ||||||
|      * Initializes the runtime environment. |  | ||||||
|      * |  | ||||||
|      * This is where you can load some file that contains filter functions for instance. |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead |  | ||||||
|      */ |  | ||||||
|     public function initRuntime(Environment $environment); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the token parser instances to add to the existing list. |      * Returns the token parser instances to add to the existing list. | ||||||
|      * |      * | ||||||
| @@ -75,24 +65,6 @@ interface ExtensionInterface | |||||||
|      * @return array<array> First array of unary operators, second array of binary operators |      * @return array<array> First array of unary operators, second array of binary operators | ||||||
|      */ |      */ | ||||||
|     public function getOperators(); |     public function getOperators(); | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Returns a list of global variables to add to the existing list. |  | ||||||
|      * |  | ||||||
|      * @return array An array of global variables |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead |  | ||||||
|      */ |  | ||||||
|     public function getGlobals(); |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Returns the name of the extension. |  | ||||||
|      * |  | ||||||
|      * @return string The extension name |  | ||||||
|      * |  | ||||||
|      * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally |  | ||||||
|      */ |  | ||||||
|     public function getName(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface'); | class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface'); | ||||||
|   | |||||||
| @@ -21,6 +21,12 @@ namespace Twig\Extension; | |||||||
|  */ |  */ | ||||||
| interface GlobalsInterface | interface GlobalsInterface | ||||||
| { | { | ||||||
|  |     /** | ||||||
|  |      * Returns a list of global variables to add to the existing list. | ||||||
|  |      * | ||||||
|  |      * @return array An array of global variables | ||||||
|  |      */ | ||||||
|  |     public function getGlobals(); | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface'); | class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface'); | ||||||
|   | |||||||
| @@ -11,6 +11,8 @@ | |||||||
|  |  | ||||||
| namespace Twig\Extension; | namespace Twig\Extension; | ||||||
|  |  | ||||||
|  | use Twig\Environment; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method. |  * Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method. | ||||||
|  * |  * | ||||||
| @@ -18,9 +20,17 @@ namespace Twig\Extension; | |||||||
|  * deprecated initRuntime() method in your extensions. |  * deprecated initRuntime() method in your extensions. | ||||||
|  * |  * | ||||||
|  * @author Fabien Potencier <fabien@symfony.com> |  * @author Fabien Potencier <fabien@symfony.com> | ||||||
|  |  * | ||||||
|  |  * @deprecated since Twig 2.7, to be removed in 3.0 | ||||||
|  */ |  */ | ||||||
| interface InitRuntimeInterface | interface InitRuntimeInterface | ||||||
| { | { | ||||||
|  |     /** | ||||||
|  |      * Initializes the runtime environment. | ||||||
|  |      * | ||||||
|  |      * This is where you can load some file that contains filter functions for instance. | ||||||
|  |      */ | ||||||
|  |     public function initRuntime(Environment $environment); | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface'); | class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface'); | ||||||
|   | |||||||
| @@ -13,12 +13,9 @@ namespace Twig\Extension; | |||||||
|  |  | ||||||
| use Twig\NodeVisitor\OptimizerNodeVisitor; | use Twig\NodeVisitor\OptimizerNodeVisitor; | ||||||
|  |  | ||||||
| /** | final class OptimizerExtension extends AbstractExtension | ||||||
|  * @final |  | ||||||
|  */ |  | ||||||
| class OptimizerExtension extends AbstractExtension |  | ||||||
| { | { | ||||||
|     protected $optimizers; |     private $optimizers; | ||||||
|  |  | ||||||
|     public function __construct($optimizers = -1) |     public function __construct($optimizers = -1) | ||||||
|     { |     { | ||||||
| @@ -29,11 +26,6 @@ class OptimizerExtension extends AbstractExtension | |||||||
|     { |     { | ||||||
|         return [new OptimizerNodeVisitor($this->optimizers)]; |         return [new OptimizerNodeVisitor($this->optimizers)]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return 'optimizer'; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer'); | class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer'); | ||||||
|   | |||||||
| @@ -41,12 +41,7 @@ class ProfilerExtension extends AbstractExtension | |||||||
|  |  | ||||||
|     public function getNodeVisitors() |     public function getNodeVisitors() | ||||||
|     { |     { | ||||||
|         return [new ProfilerNodeVisitor(\get_class($this))]; |         return [new ProfilerNodeVisitor(static::class)]; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return 'profiler'; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,17 +12,17 @@ | |||||||
| namespace Twig\Extension; | namespace Twig\Extension; | ||||||
|  |  | ||||||
| use Twig\NodeVisitor\SandboxNodeVisitor; | use Twig\NodeVisitor\SandboxNodeVisitor; | ||||||
|  | use Twig\Sandbox\SecurityNotAllowedMethodError; | ||||||
|  | use Twig\Sandbox\SecurityNotAllowedPropertyError; | ||||||
| use Twig\Sandbox\SecurityPolicyInterface; | use Twig\Sandbox\SecurityPolicyInterface; | ||||||
|  | use Twig\Source; | ||||||
| use Twig\TokenParser\SandboxTokenParser; | use Twig\TokenParser\SandboxTokenParser; | ||||||
|  |  | ||||||
| /** | final class SandboxExtension extends AbstractExtension | ||||||
|  * @final |  | ||||||
|  */ |  | ||||||
| class SandboxExtension extends AbstractExtension |  | ||||||
| { | { | ||||||
|     protected $sandboxedGlobally; |     private $sandboxedGlobally; | ||||||
|     protected $sandboxed; |     private $sandboxed; | ||||||
|     protected $policy; |     private $policy; | ||||||
|  |  | ||||||
|     public function __construct(SecurityPolicyInterface $policy, $sandboxed = false) |     public function __construct(SecurityPolicyInterface $policy, $sandboxed = false) | ||||||
|     { |     { | ||||||
| @@ -77,33 +77,49 @@ class SandboxExtension extends AbstractExtension | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function checkMethodAllowed($obj, $method) |     public function checkMethodAllowed($obj, $method, int $lineno = -1, Source $source = null) | ||||||
|     { |     { | ||||||
|         if ($this->isSandboxed()) { |         if ($this->isSandboxed()) { | ||||||
|             $this->policy->checkMethodAllowed($obj, $method); |             try { | ||||||
|  |                 $this->policy->checkMethodAllowed($obj, $method); | ||||||
|  |             } catch (SecurityNotAllowedMethodError $e) { | ||||||
|  |                 $e->setSourceContext($source); | ||||||
|  |                 $e->setTemplateLine($lineno); | ||||||
|  |  | ||||||
|  |                 throw $e; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function checkPropertyAllowed($obj, $method) |     public function checkPropertyAllowed($obj, $property, int $lineno = -1, Source $source = null) | ||||||
|     { |     { | ||||||
|         if ($this->isSandboxed()) { |         if ($this->isSandboxed()) { | ||||||
|             $this->policy->checkPropertyAllowed($obj, $method); |             try { | ||||||
|  |                 $this->policy->checkPropertyAllowed($obj, $property); | ||||||
|  |             } catch (SecurityNotAllowedPropertyError $e) { | ||||||
|  |                 $e->setSourceContext($source); | ||||||
|  |                 $e->setTemplateLine($lineno); | ||||||
|  |  | ||||||
|  |                 throw $e; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function ensureToStringAllowed($obj) |     public function ensureToStringAllowed($obj, int $lineno = -1, Source $source = null) | ||||||
|     { |     { | ||||||
|         if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) { |         if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) { | ||||||
|             $this->policy->checkMethodAllowed($obj, '__toString'); |             try { | ||||||
|  |                 $this->policy->checkMethodAllowed($obj, '__toString'); | ||||||
|  |             } catch (SecurityNotAllowedMethodError $e) { | ||||||
|  |                 $e->setSourceContext($source); | ||||||
|  |                 $e->setTemplateLine($lineno); | ||||||
|  |  | ||||||
|  |                 throw $e; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return $obj; |         return $obj; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return 'sandbox'; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox'); | class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox'); | ||||||
|   | |||||||
| @@ -13,32 +13,32 @@ namespace Twig\Extension; | |||||||
|  |  | ||||||
| use Twig\NodeVisitor\NodeVisitorInterface; | use Twig\NodeVisitor\NodeVisitorInterface; | ||||||
| use Twig\TokenParser\TokenParserInterface; | use Twig\TokenParser\TokenParserInterface; | ||||||
|  | use Twig\TwigFilter; | ||||||
|  | use Twig\TwigFunction; | ||||||
|  | use Twig\TwigTest; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Internal class. |  * Used by \Twig\Environment as a staging area. | ||||||
|  * |  | ||||||
|  * This class is used by \Twig\Environment as a staging area and must not be used directly. |  | ||||||
|  * |  * | ||||||
|  * @author Fabien Potencier <fabien@symfony.com> |  * @author Fabien Potencier <fabien@symfony.com> | ||||||
|  * |  * | ||||||
|  * @internal |  * @internal | ||||||
|  */ |  */ | ||||||
| class StagingExtension extends AbstractExtension | final class StagingExtension extends AbstractExtension | ||||||
| { | { | ||||||
|     protected $functions = []; |     private $functions = []; | ||||||
|     protected $filters = []; |     private $filters = []; | ||||||
|     protected $visitors = []; |     private $visitors = []; | ||||||
|     protected $tokenParsers = []; |     private $tokenParsers = []; | ||||||
|     protected $globals = []; |     private $tests = []; | ||||||
|     protected $tests = []; |  | ||||||
|  |  | ||||||
|     public function addFunction($name, $function) |     public function addFunction(TwigFunction $function) | ||||||
|     { |     { | ||||||
|         if (isset($this->functions[$name])) { |         if (isset($this->functions[$function->getName()])) { | ||||||
|             @trigger_error(sprintf('Overriding function "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED); |             throw new \LogicException(sprintf('Function "%s" is already registered.', $function->getName())); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->functions[$name] = $function; |         $this->functions[$function->getName()] = $function; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getFunctions() |     public function getFunctions() | ||||||
| @@ -46,13 +46,13 @@ class StagingExtension extends AbstractExtension | |||||||
|         return $this->functions; |         return $this->functions; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function addFilter($name, $filter) |     public function addFilter(TwigFilter $filter) | ||||||
|     { |     { | ||||||
|         if (isset($this->filters[$name])) { |         if (isset($this->filters[$filter->getName()])) { | ||||||
|             @trigger_error(sprintf('Overriding filter "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED); |             throw new \LogicException(sprintf('Filter "%s" is already registered.', $filter->getName())); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->filters[$name] = $filter; |         $this->filters[$filter->getName()] = $filter; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getFilters() |     public function getFilters() | ||||||
| @@ -73,7 +73,7 @@ class StagingExtension extends AbstractExtension | |||||||
|     public function addTokenParser(TokenParserInterface $parser) |     public function addTokenParser(TokenParserInterface $parser) | ||||||
|     { |     { | ||||||
|         if (isset($this->tokenParsers[$parser->getTag()])) { |         if (isset($this->tokenParsers[$parser->getTag()])) { | ||||||
|             @trigger_error(sprintf('Overriding tag "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED); |             throw new \LogicException(sprintf('Tag "%s" is already registered.', $parser->getTag())); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->tokenParsers[$parser->getTag()] = $parser; |         $this->tokenParsers[$parser->getTag()] = $parser; | ||||||
| @@ -84,34 +84,19 @@ class StagingExtension extends AbstractExtension | |||||||
|         return $this->tokenParsers; |         return $this->tokenParsers; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function addGlobal($name, $value) |     public function addTest(TwigTest $test) | ||||||
|     { |     { | ||||||
|         $this->globals[$name] = $value; |         if (isset($this->tests[$test->getName()])) { | ||||||
|     } |             throw new \LogicException(sprintf('Test "%s" is already registered.', $test->getName())); | ||||||
|  |  | ||||||
|     public function getGlobals() |  | ||||||
|     { |  | ||||||
|         return $this->globals; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function addTest($name, $test) |  | ||||||
|     { |  | ||||||
|         if (isset($this->tests[$name])) { |  | ||||||
|             @trigger_error(sprintf('Overriding test "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->tests[$name] = $test; |         $this->tests[$test->getName()] = $test; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getTests() |     public function getTests() | ||||||
|     { |     { | ||||||
|         return $this->tests; |         return $this->tests; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return 'staging'; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging'); | class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging'); | ||||||
|   | |||||||
| @@ -12,10 +12,7 @@ | |||||||
| namespace Twig\Extension { | namespace Twig\Extension { | ||||||
| use Twig\TwigFunction; | use Twig\TwigFunction; | ||||||
|  |  | ||||||
| /** | final class StringLoaderExtension extends AbstractExtension | ||||||
|  * @final |  | ||||||
|  */ |  | ||||||
| class StringLoaderExtension extends AbstractExtension |  | ||||||
| { | { | ||||||
|     public function getFunctions() |     public function getFunctions() | ||||||
|     { |     { | ||||||
| @@ -23,11 +20,6 @@ class StringLoaderExtension extends AbstractExtension | |||||||
|             new TwigFunction('template_from_string', 'twig_template_from_string', ['needs_environment' => true]), |             new TwigFunction('template_from_string', 'twig_template_from_string', ['needs_environment' => true]), | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getName() |  | ||||||
|     { |  | ||||||
|         return 'string_loader'; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class_alias('Twig\Extension\StringLoaderExtension', 'Twig_Extension_StringLoader'); | class_alias('Twig\Extension\StringLoaderExtension', 'Twig_Extension_StringLoader'); | ||||||
| @@ -47,7 +39,7 @@ use Twig\TemplateWrapper; | |||||||
|  * |  * | ||||||
|  * @return TemplateWrapper |  * @return TemplateWrapper | ||||||
|  */ |  */ | ||||||
| function twig_template_from_string(Environment $env, $template, $name = null) | function twig_template_from_string(Environment $env, $template, string $name = null) | ||||||
| { | { | ||||||
|     return $env->createTemplate((string) $template, $name); |     return $env->createTemplate((string) $template, $name); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										475
									
								
								system/libs/Twig/ExtensionSet.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								system/libs/Twig/ExtensionSet.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,475 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * This file is part of Twig. | ||||||
|  |  * | ||||||
|  |  * (c) Fabien Potencier | ||||||
|  |  * | ||||||
|  |  * For the full copyright and license information, please view the LICENSE | ||||||
|  |  * file that was distributed with this source code. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | namespace Twig; | ||||||
|  |  | ||||||
|  | use Twig\Error\RuntimeError; | ||||||
|  | use Twig\Extension\ExtensionInterface; | ||||||
|  | use Twig\Extension\GlobalsInterface; | ||||||
|  | use Twig\Extension\InitRuntimeInterface; | ||||||
|  | use Twig\Extension\StagingExtension; | ||||||
|  | use Twig\NodeVisitor\NodeVisitorInterface; | ||||||
|  | use Twig\TokenParser\TokenParserInterface; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @author Fabien Potencier <fabien@symfony.com> | ||||||
|  |  * | ||||||
|  |  * @internal | ||||||
|  |  */ | ||||||
|  | final class ExtensionSet | ||||||
|  | { | ||||||
|  |     private $extensions; | ||||||
|  |     private $initialized = false; | ||||||
|  |     private $runtimeInitialized = false; | ||||||
|  |     private $staging; | ||||||
|  |     private $parsers; | ||||||
|  |     private $visitors; | ||||||
|  |     private $filters; | ||||||
|  |     private $tests; | ||||||
|  |     private $functions; | ||||||
|  |     private $unaryOperators; | ||||||
|  |     private $binaryOperators; | ||||||
|  |     private $globals; | ||||||
|  |     private $functionCallbacks = []; | ||||||
|  |     private $filterCallbacks = []; | ||||||
|  |     private $lastModified = 0; | ||||||
|  |  | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  |         $this->staging = new StagingExtension(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Initializes the runtime environment. | ||||||
|  |      * | ||||||
|  |      * @deprecated since Twig 2.7 | ||||||
|  |      */ | ||||||
|  |     public function initRuntime(Environment $env) | ||||||
|  |     { | ||||||
|  |         if ($this->runtimeInitialized) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->runtimeInitialized = true; | ||||||
|  |  | ||||||
|  |         foreach ($this->extensions as $extension) { | ||||||
|  |             if ($extension instanceof InitRuntimeInterface) { | ||||||
|  |                 $extension->initRuntime($env); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function hasExtension(string $class): bool | ||||||
|  |     { | ||||||
|  |         $class = ltrim($class, '\\'); | ||||||
|  |         if (!isset($this->extensions[$class]) && class_exists($class, false)) { | ||||||
|  |             // For BC/FC with namespaced aliases | ||||||
|  |             $class = (new \ReflectionClass($class))->name; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return isset($this->extensions[$class]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getExtension(string $class): ExtensionInterface | ||||||
|  |     { | ||||||
|  |         $class = ltrim($class, '\\'); | ||||||
|  |         if (!isset($this->extensions[$class]) && class_exists($class, false)) { | ||||||
|  |             // For BC/FC with namespaced aliases | ||||||
|  |             $class = (new \ReflectionClass($class))->name; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!isset($this->extensions[$class])) { | ||||||
|  |             throw new RuntimeError(sprintf('The "%s" extension is not enabled.', $class)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->extensions[$class]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param ExtensionInterface[] $extensions | ||||||
|  |      */ | ||||||
|  |     public function setExtensions(array $extensions) | ||||||
|  |     { | ||||||
|  |         foreach ($extensions as $extension) { | ||||||
|  |             $this->addExtension($extension); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return ExtensionInterface[] | ||||||
|  |      */ | ||||||
|  |     public function getExtensions(): array | ||||||
|  |     { | ||||||
|  |         return $this->extensions; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getSignature(): string | ||||||
|  |     { | ||||||
|  |         return json_encode(array_keys($this->extensions)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function isInitialized(): bool | ||||||
|  |     { | ||||||
|  |         return $this->initialized || $this->runtimeInitialized; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getLastModified(): int | ||||||
|  |     { | ||||||
|  |         if (0 !== $this->lastModified) { | ||||||
|  |             return $this->lastModified; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->extensions as $extension) { | ||||||
|  |             $r = new \ReflectionObject($extension); | ||||||
|  |             if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModified) { | ||||||
|  |                 $this->lastModified = $extensionTime; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->lastModified; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addExtension(ExtensionInterface $extension) | ||||||
|  |     { | ||||||
|  |         $class = \get_class($extension); | ||||||
|  |  | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             throw new \LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (isset($this->extensions[$class])) { | ||||||
|  |             throw new \LogicException(sprintf('Unable to register extension "%s" as it is already registered.', $class)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->extensions[$class] = $extension; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addFunction(TwigFunction $function) | ||||||
|  |     { | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->staging->addFunction($function); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TwigFunction[] | ||||||
|  |      */ | ||||||
|  |     public function getFunctions(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->functions; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TwigFunction|false | ||||||
|  |      */ | ||||||
|  |     public function getFunction(string $name) | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (isset($this->functions[$name])) { | ||||||
|  |             return $this->functions[$name]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->functions as $pattern => $function) { | ||||||
|  |             $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); | ||||||
|  |  | ||||||
|  |             if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) { | ||||||
|  |                 array_shift($matches); | ||||||
|  |                 $function->setArguments($matches); | ||||||
|  |  | ||||||
|  |                 return $function; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->functionCallbacks as $callback) { | ||||||
|  |             if (false !== $function = $callback($name)) { | ||||||
|  |                 return $function; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function registerUndefinedFunctionCallback(callable $callable) | ||||||
|  |     { | ||||||
|  |         $this->functionCallbacks[] = $callable; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addFilter(TwigFilter $filter) | ||||||
|  |     { | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             throw new \LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->staging->addFilter($filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TwigFilter[] | ||||||
|  |      */ | ||||||
|  |     public function getFilters(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->filters; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TwigFilter|false | ||||||
|  |      */ | ||||||
|  |     public function getFilter(string $name) | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (isset($this->filters[$name])) { | ||||||
|  |             return $this->filters[$name]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->filters as $pattern => $filter) { | ||||||
|  |             $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); | ||||||
|  |  | ||||||
|  |             if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) { | ||||||
|  |                 array_shift($matches); | ||||||
|  |                 $filter->setArguments($matches); | ||||||
|  |  | ||||||
|  |                 return $filter; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->filterCallbacks as $callback) { | ||||||
|  |             if (false !== $filter = $callback($name)) { | ||||||
|  |                 return $filter; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function registerUndefinedFilterCallback(callable $callable) | ||||||
|  |     { | ||||||
|  |         $this->filterCallbacks[] = $callable; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addNodeVisitor(NodeVisitorInterface $visitor) | ||||||
|  |     { | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->staging->addNodeVisitor($visitor); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return NodeVisitorInterface[] | ||||||
|  |      */ | ||||||
|  |     public function getNodeVisitors(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->visitors; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addTokenParser(TokenParserInterface $parser) | ||||||
|  |     { | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             throw new \LogicException('Unable to add a token parser as extensions have already been initialized.'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->staging->addTokenParser($parser); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TokenParserInterface[] | ||||||
|  |      */ | ||||||
|  |     public function getTokenParsers(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->parsers; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getGlobals(): array | ||||||
|  |     { | ||||||
|  |         if (null !== $this->globals) { | ||||||
|  |             return $this->globals; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $globals = []; | ||||||
|  |         foreach ($this->extensions as $extension) { | ||||||
|  |             if (!$extension instanceof GlobalsInterface) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $extGlobals = $extension->getGlobals(); | ||||||
|  |             if (!\is_array($extGlobals)) { | ||||||
|  |                 throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension))); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $globals = array_merge($globals, $extGlobals); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             $this->globals = $globals; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $globals; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addTest(TwigTest $test) | ||||||
|  |     { | ||||||
|  |         if ($this->initialized) { | ||||||
|  |             throw new \LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->staging->addTest($test); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TwigTest[] | ||||||
|  |      */ | ||||||
|  |     public function getTests(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->tests; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return TwigTest|false | ||||||
|  |      */ | ||||||
|  |     public function getTest(string $name) | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (isset($this->tests[$name])) { | ||||||
|  |             return $this->tests[$name]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->tests as $pattern => $test) { | ||||||
|  |             $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); | ||||||
|  |  | ||||||
|  |             if ($count) { | ||||||
|  |                 if (preg_match('#^'.$pattern.'$#', $name, $matches)) { | ||||||
|  |                     array_shift($matches); | ||||||
|  |                     $test->setArguments($matches); | ||||||
|  |  | ||||||
|  |                     return $test; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getUnaryOperators(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->unaryOperators; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getBinaryOperators(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->initialized) { | ||||||
|  |             $this->initExtensions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->binaryOperators; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function initExtensions() | ||||||
|  |     { | ||||||
|  |         $this->parsers = []; | ||||||
|  |         $this->filters = []; | ||||||
|  |         $this->functions = []; | ||||||
|  |         $this->tests = []; | ||||||
|  |         $this->visitors = []; | ||||||
|  |         $this->unaryOperators = []; | ||||||
|  |         $this->binaryOperators = []; | ||||||
|  |  | ||||||
|  |         foreach ($this->extensions as $extension) { | ||||||
|  |             $this->initExtension($extension); | ||||||
|  |         } | ||||||
|  |         $this->initExtension($this->staging); | ||||||
|  |         // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception | ||||||
|  |         $this->initialized = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function initExtension(ExtensionInterface $extension) | ||||||
|  |     { | ||||||
|  |         // filters | ||||||
|  |         foreach ($extension->getFilters() as $filter) { | ||||||
|  |             $this->filters[$filter->getName()] = $filter; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // functions | ||||||
|  |         foreach ($extension->getFunctions() as $function) { | ||||||
|  |             $this->functions[$function->getName()] = $function; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // tests | ||||||
|  |         foreach ($extension->getTests() as $test) { | ||||||
|  |             $this->tests[$test->getName()] = $test; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // token parsers | ||||||
|  |         foreach ($extension->getTokenParsers() as $parser) { | ||||||
|  |             if (!$parser instanceof TokenParserInterface) { | ||||||
|  |                 throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.'); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $this->parsers[] = $parser; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // node visitors | ||||||
|  |         foreach ($extension->getNodeVisitors() as $visitor) { | ||||||
|  |             $this->visitors[] = $visitor; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // operators | ||||||
|  |         if ($operators = $extension->getOperators()) { | ||||||
|  |             if (!\is_array($operators)) { | ||||||
|  |                 throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', \get_class($extension), \is_object($operators) ? \get_class($operators) : \gettype($operators).(\is_resource($operators) ? '' : '#'.$operators))); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (2 !== \count($operators)) { | ||||||
|  |                 throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators))); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); | ||||||
|  |             $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class_alias('Twig\ExtensionSet', 'Twig_ExtensionSet'); | ||||||
| @@ -41,7 +41,7 @@ class FileExtensionEscapingStrategy | |||||||
|             $name = substr($name, 0, -5); |             $name = substr($name, 0, -5); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $extension = pathinfo($name, PATHINFO_EXTENSION); |         $extension = pathinfo($name, \PATHINFO_EXTENSION); | ||||||
|  |  | ||||||
|         switch ($extension) { |         switch ($extension) { | ||||||
|             case 'js': |             case 'js': | ||||||
|   | |||||||
| @@ -19,39 +19,36 @@ use Twig\Error\SyntaxError; | |||||||
|  * |  * | ||||||
|  * @author Fabien Potencier <fabien@symfony.com> |  * @author Fabien Potencier <fabien@symfony.com> | ||||||
|  */ |  */ | ||||||
| class Lexer implements \Twig_LexerInterface | class Lexer | ||||||
| { | { | ||||||
|     protected $tokens; |     private $tokens; | ||||||
|     protected $code; |     private $code; | ||||||
|     protected $cursor; |     private $cursor; | ||||||
|     protected $lineno; |     private $lineno; | ||||||
|     protected $end; |     private $end; | ||||||
|     protected $state; |     private $state; | ||||||
|     protected $states; |     private $states; | ||||||
|     protected $brackets; |     private $brackets; | ||||||
|     protected $env; |     private $env; | ||||||
|     // to be renamed to $name in 2.0 (where it is private) |  | ||||||
|     protected $filename; |  | ||||||
|     protected $options; |  | ||||||
|     protected $regexes; |  | ||||||
|     protected $position; |  | ||||||
|     protected $positions; |  | ||||||
|     protected $currentVarBlockLine; |  | ||||||
|  |  | ||||||
|     private $source; |     private $source; | ||||||
|  |     private $options; | ||||||
|  |     private $regexes; | ||||||
|  |     private $position; | ||||||
|  |     private $positions; | ||||||
|  |     private $currentVarBlockLine; | ||||||
|  |  | ||||||
|     const STATE_DATA = 0; |     public const STATE_DATA = 0; | ||||||
|     const STATE_BLOCK = 1; |     public const STATE_BLOCK = 1; | ||||||
|     const STATE_VAR = 2; |     public const STATE_VAR = 2; | ||||||
|     const STATE_STRING = 3; |     public const STATE_STRING = 3; | ||||||
|     const STATE_INTERPOLATION = 4; |     public const STATE_INTERPOLATION = 4; | ||||||
|  |  | ||||||
|     const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'; |     public const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'; | ||||||
|     const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A'; |     public const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A'; | ||||||
|     const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; |     public const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; | ||||||
|     const REGEX_DQ_STRING_DELIM = '/"/A'; |     public const REGEX_DQ_STRING_DELIM = '/"/A'; | ||||||
|     const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; |     public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; | ||||||
|     const PUNCTUATION = '()[]{}?:.,|'; |     public const PUNCTUATION = '()[]{}?:.,|'; | ||||||
|  |  | ||||||
|     public function __construct(Environment $env, array $options = []) |     public function __construct(Environment $env, array $options = []) | ||||||
|     { |     { | ||||||
| @@ -100,9 +97,7 @@ class Lexer implements \Twig_LexerInterface | |||||||
|                     $this->options['whitespace_trim']. // - |                     $this->options['whitespace_trim']. // - | ||||||
|                     '|'. |                     '|'. | ||||||
|                     $this->options['whitespace_line_trim']. // ~ |                     $this->options['whitespace_line_trim']. // ~ | ||||||
|                 ')?\s*'. |                 ')?\s*endverbatim\s*'. | ||||||
|                 '(?:end%s)'. // endraw or endverbatim |  | ||||||
|                 '\s*'. |  | ||||||
|                 '(?:'. |                 '(?:'. | ||||||
|                     preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%} |                     preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%} | ||||||
|                     '|'. |                     '|'. | ||||||
| @@ -117,7 +112,7 @@ class Lexer implements \Twig_LexerInterface | |||||||
|             // #} |             // #} | ||||||
|             'lex_comment' => '{ |             'lex_comment' => '{ | ||||||
|                 (?:'. |                 (?:'. | ||||||
|                     preg_quote($this->options['whitespace_trim']).preg_quote($this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n? |                     preg_quote($this->options['whitespace_trim'].$this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n? | ||||||
|                     '|'. |                     '|'. | ||||||
|                     preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]* |                     preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]* | ||||||
|                     '|'. |                     '|'. | ||||||
| @@ -127,9 +122,7 @@ class Lexer implements \Twig_LexerInterface | |||||||
|  |  | ||||||
|             // verbatim %} |             // verbatim %} | ||||||
|             'lex_block_raw' => '{ |             'lex_block_raw' => '{ | ||||||
|                 \s* |                 \s*verbatim\s* | ||||||
|                 (raw|verbatim) |  | ||||||
|                 \s* |  | ||||||
|                 (?:'. |                 (?:'. | ||||||
|                     preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}\s* |                     preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}\s* | ||||||
|                     '|'. |                     '|'. | ||||||
| @@ -160,28 +153,10 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function tokenize($code, $name = null) |     public function tokenize(Source $source) | ||||||
|     { |     { | ||||||
|         if (!$code instanceof Source) { |         $this->source = $source; | ||||||
|             @trigger_error(sprintf('Passing a string as the $code argument of %s() is deprecated since version 1.27 and will be removed in 2.0. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED); |         $this->code = str_replace(["\r\n", "\r"], "\n", $source->getCode()); | ||||||
|             $this->source = new Source($code, $name); |  | ||||||
|         } else { |  | ||||||
|             $this->source = $code; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (((int) ini_get('mbstring.func_overload')) & 2) { |  | ||||||
|             @trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (\function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { |  | ||||||
|             $mbEncoding = mb_internal_encoding(); |  | ||||||
|             mb_internal_encoding('ASCII'); |  | ||||||
|         } else { |  | ||||||
|             $mbEncoding = null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $this->code = str_replace(["\r\n", "\r"], "\n", $this->source->getCode()); |  | ||||||
|         $this->filename = $this->source->getName(); |  | ||||||
|         $this->cursor = 0; |         $this->cursor = 0; | ||||||
|         $this->lineno = 1; |         $this->lineno = 1; | ||||||
|         $this->end = \strlen($this->code); |         $this->end = \strlen($this->code); | ||||||
| @@ -192,7 +167,7 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         $this->position = -1; |         $this->position = -1; | ||||||
|  |  | ||||||
|         // find all token starts in one go |         // find all token starts in one go | ||||||
|         preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE); |         preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, \PREG_OFFSET_CAPTURE); | ||||||
|         $this->positions = $matches; |         $this->positions = $matches; | ||||||
|  |  | ||||||
|         while ($this->cursor < $this->end) { |         while ($this->cursor < $this->end) { | ||||||
| @@ -221,25 +196,21 @@ class Lexer implements \Twig_LexerInterface | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->pushToken(Token::EOF_TYPE); |         $this->pushToken(/* Token::EOF_TYPE */ -1); | ||||||
|  |  | ||||||
|         if (!empty($this->brackets)) { |         if (!empty($this->brackets)) { | ||||||
|             list($expect, $lineno) = array_pop($this->brackets); |             list($expect, $lineno) = array_pop($this->brackets); | ||||||
|             throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); |             throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if ($mbEncoding) { |  | ||||||
|             mb_internal_encoding($mbEncoding); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return new TokenStream($this->tokens, $this->source); |         return new TokenStream($this->tokens, $this->source); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexData() |     private function lexData() | ||||||
|     { |     { | ||||||
|         // if no matches are left we return the rest of the template as simple text token |         // if no matches are left we return the rest of the template as simple text token | ||||||
|         if ($this->position == \count($this->positions[0]) - 1) { |         if ($this->position == \count($this->positions[0]) - 1) { | ||||||
|             $this->pushToken(Token::TEXT_TYPE, substr($this->code, $this->cursor)); |             $this->pushToken(/* Token::TEXT_TYPE */ 0, substr($this->code, $this->cursor)); | ||||||
|             $this->cursor = $this->end; |             $this->cursor = $this->end; | ||||||
|  |  | ||||||
|             return; |             return; | ||||||
| @@ -268,7 +239,7 @@ class Lexer implements \Twig_LexerInterface | |||||||
|                 $text = rtrim($text, " \t\0\x0B"); |                 $text = rtrim($text, " \t\0\x0B"); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         $this->pushToken(Token::TEXT_TYPE, $text); |         $this->pushToken(/* Token::TEXT_TYPE */ 0, $text); | ||||||
|         $this->moveCursor($textContent.$position[0]); |         $this->moveCursor($textContent.$position[0]); | ||||||
|  |  | ||||||
|         switch ($this->positions[1][$this->position][0]) { |         switch ($this->positions[1][$this->position][0]) { | ||||||
| @@ -280,30 +251,30 @@ class Lexer implements \Twig_LexerInterface | |||||||
|                 // raw data? |                 // raw data? | ||||||
|                 if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, 0, $this->cursor)) { |                 if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, 0, $this->cursor)) { | ||||||
|                     $this->moveCursor($match[0]); |                     $this->moveCursor($match[0]); | ||||||
|                     $this->lexRawData($match[1]); |                     $this->lexRawData(); | ||||||
|                 // {% line \d+ %} |                 // {% line \d+ %} | ||||||
|                 } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, 0, $this->cursor)) { |                 } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, 0, $this->cursor)) { | ||||||
|                     $this->moveCursor($match[0]); |                     $this->moveCursor($match[0]); | ||||||
|                     $this->lineno = (int) $match[1]; |                     $this->lineno = (int) $match[1]; | ||||||
|                 } else { |                 } else { | ||||||
|                     $this->pushToken(Token::BLOCK_START_TYPE); |                     $this->pushToken(/* Token::BLOCK_START_TYPE */ 1); | ||||||
|                     $this->pushState(self::STATE_BLOCK); |                     $this->pushState(self::STATE_BLOCK); | ||||||
|                     $this->currentVarBlockLine = $this->lineno; |                     $this->currentVarBlockLine = $this->lineno; | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|             case $this->options['tag_variable'][0]: |             case $this->options['tag_variable'][0]: | ||||||
|                 $this->pushToken(Token::VAR_START_TYPE); |                 $this->pushToken(/* Token::VAR_START_TYPE */ 2); | ||||||
|                 $this->pushState(self::STATE_VAR); |                 $this->pushState(self::STATE_VAR); | ||||||
|                 $this->currentVarBlockLine = $this->lineno; |                 $this->currentVarBlockLine = $this->lineno; | ||||||
|                 break; |                 break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexBlock() |     private function lexBlock() | ||||||
|     { |     { | ||||||
|         if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) { |         if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) { | ||||||
|             $this->pushToken(Token::BLOCK_END_TYPE); |             $this->pushToken(/* Token::BLOCK_END_TYPE */ 3); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|             $this->popState(); |             $this->popState(); | ||||||
|         } else { |         } else { | ||||||
| @@ -311,10 +282,10 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexVar() |     private function lexVar() | ||||||
|     { |     { | ||||||
|         if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) { |         if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) { | ||||||
|             $this->pushToken(Token::VAR_END_TYPE); |             $this->pushToken(/* Token::VAR_END_TYPE */ 4); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|             $this->popState(); |             $this->popState(); | ||||||
|         } else { |         } else { | ||||||
| @@ -322,7 +293,7 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexExpression() |     private function lexExpression() | ||||||
|     { |     { | ||||||
|         // whitespace |         // whitespace | ||||||
|         if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) { |         if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) { | ||||||
| @@ -340,21 +311,21 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         } |         } | ||||||
|         // operators |         // operators | ||||||
|         elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) { |         elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) { | ||||||
|             $this->pushToken(Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0])); |             $this->pushToken(/* Token::OPERATOR_TYPE */ 8, preg_replace('/\s+/', ' ', $match[0])); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|         } |         } | ||||||
|         // names |         // names | ||||||
|         elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) { |         elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) { | ||||||
|             $this->pushToken(Token::NAME_TYPE, $match[0]); |             $this->pushToken(/* Token::NAME_TYPE */ 5, $match[0]); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|         } |         } | ||||||
|         // numbers |         // numbers | ||||||
|         elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) { |         elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) { | ||||||
|             $number = (float) $match[0];  // floats |             $number = (float) $match[0];  // floats | ||||||
|             if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) { |             if (ctype_digit($match[0]) && $number <= \PHP_INT_MAX) { | ||||||
|                 $number = (int) $match[0]; // integers lower than the maximum |                 $number = (int) $match[0]; // integers lower than the maximum | ||||||
|             } |             } | ||||||
|             $this->pushToken(Token::NUMBER_TYPE, $number); |             $this->pushToken(/* Token::NUMBER_TYPE */ 6, $number); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|         } |         } | ||||||
|         // punctuation |         // punctuation | ||||||
| @@ -375,12 +346,12 @@ class Lexer implements \Twig_LexerInterface | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             $this->pushToken(Token::PUNCTUATION_TYPE, $this->code[$this->cursor]); |             $this->pushToken(/* Token::PUNCTUATION_TYPE */ 9, $this->code[$this->cursor]); | ||||||
|             ++$this->cursor; |             ++$this->cursor; | ||||||
|         } |         } | ||||||
|         // strings |         // strings | ||||||
|         elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) { |         elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) { | ||||||
|             $this->pushToken(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1))); |             $this->pushToken(/* Token::STRING_TYPE */ 7, stripcslashes(substr($match[0], 1, -1))); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|         } |         } | ||||||
|         // opening double quoted string |         // opening double quoted string | ||||||
| @@ -395,14 +366,10 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexRawData($tag) |     private function lexRawData() | ||||||
|     { |     { | ||||||
|         if ('raw' === $tag) { |         if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) { | ||||||
|             @trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED); |             throw new SyntaxError('Unexpected end of file: Unclosed "verbatim" block.', $this->lineno, $this->source); | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { |  | ||||||
|             throw new SyntaxError(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->source); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor); |         $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor); | ||||||
| @@ -420,27 +387,27 @@ class Lexer implements \Twig_LexerInterface | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->pushToken(Token::TEXT_TYPE, $text); |         $this->pushToken(/* Token::TEXT_TYPE */ 0, $text); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexComment() |     private function lexComment() | ||||||
|     { |     { | ||||||
|         if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { |         if (!preg_match($this->regexes['lex_comment'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) { | ||||||
|             throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source); |             throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]); |         $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexString() |     private function lexString() | ||||||
|     { |     { | ||||||
|         if (preg_match($this->regexes['interpolation_start'], $this->code, $match, 0, $this->cursor)) { |         if (preg_match($this->regexes['interpolation_start'], $this->code, $match, 0, $this->cursor)) { | ||||||
|             $this->brackets[] = [$this->options['interpolation'][0], $this->lineno]; |             $this->brackets[] = [$this->options['interpolation'][0], $this->lineno]; | ||||||
|             $this->pushToken(Token::INTERPOLATION_START_TYPE); |             $this->pushToken(/* Token::INTERPOLATION_START_TYPE */ 10); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|             $this->pushState(self::STATE_INTERPOLATION); |             $this->pushState(self::STATE_INTERPOLATION); | ||||||
|         } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && \strlen($match[0]) > 0) { |         } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && \strlen($match[0]) > 0) { | ||||||
|             $this->pushToken(Token::STRING_TYPE, stripcslashes($match[0])); |             $this->pushToken(/* Token::STRING_TYPE */ 7, stripcslashes($match[0])); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|         } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) { |         } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) { | ||||||
|             list($expect, $lineno) = array_pop($this->brackets); |             list($expect, $lineno) = array_pop($this->brackets); | ||||||
| @@ -456,12 +423,12 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function lexInterpolation() |     private function lexInterpolation() | ||||||
|     { |     { | ||||||
|         $bracket = end($this->brackets); |         $bracket = end($this->brackets); | ||||||
|         if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, 0, $this->cursor)) { |         if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, 0, $this->cursor)) { | ||||||
|             array_pop($this->brackets); |             array_pop($this->brackets); | ||||||
|             $this->pushToken(Token::INTERPOLATION_END_TYPE); |             $this->pushToken(/* Token::INTERPOLATION_END_TYPE */ 11); | ||||||
|             $this->moveCursor($match[0]); |             $this->moveCursor($match[0]); | ||||||
|             $this->popState(); |             $this->popState(); | ||||||
|         } else { |         } else { | ||||||
| @@ -469,23 +436,23 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function pushToken($type, $value = '') |     private function pushToken($type, $value = '') | ||||||
|     { |     { | ||||||
|         // do not push empty text tokens |         // do not push empty text tokens | ||||||
|         if (Token::TEXT_TYPE === $type && '' === $value) { |         if (/* Token::TEXT_TYPE */ 0 === $type && '' === $value) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $this->tokens[] = new Token($type, $value, $this->lineno); |         $this->tokens[] = new Token($type, $value, $this->lineno); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function moveCursor($text) |     private function moveCursor($text) | ||||||
|     { |     { | ||||||
|         $this->cursor += \strlen($text); |         $this->cursor += \strlen($text); | ||||||
|         $this->lineno += substr_count($text, "\n"); |         $this->lineno += substr_count($text, "\n"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function getOperatorRegex() |     private function getOperatorRegex() | ||||||
|     { |     { | ||||||
|         $operators = array_merge( |         $operators = array_merge( | ||||||
|             ['='], |             ['='], | ||||||
| @@ -499,11 +466,15 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         $regex = []; |         $regex = []; | ||||||
|         foreach ($operators as $operator => $length) { |         foreach ($operators as $operator => $length) { | ||||||
|             // an operator that ends with a character must be followed by |             // an operator that ends with a character must be followed by | ||||||
|             // a whitespace or a parenthesis |             // a whitespace, a parenthesis, an opening map [ or sequence { | ||||||
|  |             $r = preg_quote($operator, '/'); | ||||||
|             if (ctype_alpha($operator[$length - 1])) { |             if (ctype_alpha($operator[$length - 1])) { | ||||||
|                 $r = preg_quote($operator, '/').'(?=[\s()])'; |                 $r .= '(?=[\s()\[{])'; | ||||||
|             } else { |             } | ||||||
|                 $r = preg_quote($operator, '/'); |  | ||||||
|  |             // an operator that begins with a character must not have a dot or pipe before | ||||||
|  |             if (ctype_alpha($operator[0])) { | ||||||
|  |                 $r = '(?<![\.\|])'.$r; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             // an operator with a space can be any amount of whitespaces |             // an operator with a space can be any amount of whitespaces | ||||||
| @@ -515,13 +486,13 @@ class Lexer implements \Twig_LexerInterface | |||||||
|         return '/'.implode('|', $regex).'/A'; |         return '/'.implode('|', $regex).'/A'; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function pushState($state) |     private function pushState($state) | ||||||
|     { |     { | ||||||
|         $this->states[] = $this->state; |         $this->states[] = $this->state; | ||||||
|         $this->state = $state; |         $this->state = $state; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected function popState() |     private function popState() | ||||||
|     { |     { | ||||||
|         if (0 === \count($this->states)) { |         if (0 === \count($this->states)) { | ||||||
|             throw new \LogicException('Cannot pop state without a previous state.'); |             throw new \LogicException('Cannot pop state without a previous state.'); | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user