Merge branch 'develop' into feature/admin-bar
@ -11,4 +11,6 @@ insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
indent_style = tab
|
||||
|
||||
[composer.json]
|
||||
indent_style = space
|
8
.gitignore
vendored
@ -3,6 +3,10 @@ Thumbs.db
|
||||
.idea
|
||||
tmp
|
||||
|
||||
# composer
|
||||
composer.lock
|
||||
vendor
|
||||
|
||||
releases
|
||||
config.local.php
|
||||
PERSONAL_NOTES
|
||||
@ -38,6 +42,10 @@ plugins/*
|
||||
!plugins/account-create-hint.json
|
||||
!plugins/account-create-hint
|
||||
landing
|
||||
/login.php
|
||||
|
||||
# system
|
||||
system/functions_custom.php
|
||||
|
||||
# others/rest
|
||||
system/pages/downloads.php
|
||||
|
@ -1,19 +1,18 @@
|
||||
|
||||
language: php
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4
|
||||
- 8.0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
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:
|
||||
- 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.php" .
|
||||
|
647
CHANGELOG.md
@ -1,652 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## [0.8.2 - x.x.2020]
|
||||
## [0.9.0 - x.x.2020]
|
||||
|
||||
### Added
|
||||
* $_SERVER['REQUEST_URI'] to database.log
|
||||
|
||||
### Changed
|
||||
* account_login input type from password to text
|
||||
|
||||
### Fixed
|
||||
* Updating template menus on template change
|
||||
* Account change info when config.account_country is disabled
|
||||
* Show character indicator in check_name.js
|
||||
* Houses list View button
|
||||
* Fix OTS_House houseid parameter
|
||||
|
||||
## [0.8.1 - 10.03.2020]
|
||||
|
||||
### Added
|
||||
* Support for Nostalrius OTS
|
||||
|
||||
### Changed
|
||||
* Move TODO to wiki
|
||||
* .tooltip css class to .item_image (bootstrap conflict)
|
||||
|
||||
### Fixed
|
||||
* Reloading of creatures/monsters throwing an exception
|
||||
* Loading custom pages with old Gesior variables [#108](https://github.com/slawkens/myaac/issues/107)
|
||||
* Some weird behaviour with installation of plugins
|
||||
* CHANGELOG.md loading in Admin Panel
|
||||
* spells displaying when level = 0
|
||||
* Some PHP warnings and notices
|
||||
|
||||
## [0.8.0 - 19.02.2020]
|
||||
|
||||
### Added:
|
||||
* new Awesome Bootstrap Admin Panel by Lee (@Leesneaks)
|
||||
* using Bootstrap 3
|
||||
* all existing pages were adjusted
|
||||
* new editor: Accounts
|
||||
* improved editor: Players
|
||||
* new Reports View page
|
||||
* Modules directory, which can be added using Plugins (@Leesneaks, @whiteblXK)
|
||||
* move News Management here (@whiteblXK)
|
||||
* interactive player outfit chooser (@tobi132)
|
||||
* added Highscores by balance
|
||||
* possibility to define colors and "Open in New Tab" on Template Menus (needs to be supported by Template)
|
||||
* support for database persistent and socket connections (performance boost)
|
||||
* Team page - display outfits of the players (configurable)
|
||||
* added clear_cache.php, send_email.php bin commands (@slawkens, @tobi132)
|
||||
* added locale pt_br (@ivenspontes)
|
||||
* added load time into items & weapons loading admin page
|
||||
* new, beautiful exception handler
|
||||
* added travisci to prevent mistype (@gpedro, #89)
|
||||
* added showing database name into installation script (@tobi132)
|
||||
* compatibility with old z_ gesior table (@tobi132, #46)
|
||||
* added nginx-sample.conf, .editorconfig, VERSION
|
||||
* database towns table support for TFS 1.3 (@tobi132)
|
||||
* added enable_tinymce option to Pages editor
|
||||
|
||||
### Fixed:
|
||||
* account login redirect with special chars (like '&' and '?')
|
||||
* black skull info at serverInfo (@tornadia)
|
||||
* set correct limit at lastkills page from config (anyeor from OtLand)
|
||||
* myaac_monsters table column loot problem (#79)
|
||||
* players column deleted install description (@gpedro, #91)
|
||||
* experience table being to wide and buggy on some templates (@tobi132, #90)
|
||||
* fix errors with .htaccess files
|
||||
* added index.html to prevent indexing the folder by mod_index
|
||||
|
||||
### Changed:
|
||||
* Environment is now configurable by env setting (Significantly better load times with 'prod')
|
||||
* replace spells, monsters tables with JavaScript Sortable Tables - DataTables (@Leesneaks)
|
||||
* change default MySQL Storage Engine to InnoDB and Default Character Set to utf8
|
||||
* updated OTS_House class to support latest TFS 1.x (new columns)
|
||||
* updated monster images to the original ones from tibia.com
|
||||
* increased the minimum length (3 -> 4) and decreased the maximum length (25 -> 21) of the New Character Name (by @vankk)
|
||||
* use $db->exec instead of $db->query optimisation
|
||||
* move items from database to Cache_PHP (Much more faster load time)
|
||||
* allow simultaneous loading of config.ini and config.php in templates
|
||||
* updated copyright year and SSL link (@EPuncker, #88)
|
||||
* move commands, rules and downloads pages into database (@tobi132)
|
||||
* better view of guilds (new buttons, table look and feel) (@tobi132)
|
||||
* remove stupid alerts on account create
|
||||
* remove .dist extension from .htaccess
|
||||
|
||||
### New Configurables (config.php)
|
||||
* env (Environment)
|
||||
* account_create_auto_login (Auto Login after Create Account - Registration)
|
||||
* account_create_character_create (Create Character directly on Create Account page) (@tobi132)
|
||||
* footer_show_load_time (display load time of the page in the footer)
|
||||
* database_socket (Connection via Unix Socket)
|
||||
* database_persistent (Database Persistent Connection)
|
||||
* database_log (Logging of Database Queries)
|
||||
* admin_panel_modules (Modules displayed in Admin Panel Dashboard)
|
||||
* status_timeout, status_interval
|
||||
* smtp_debug (More info about SMTP errors in error.log)
|
||||
* team_display_outfit (Display outfit of the team members on teams page)
|
||||
* highscores_balance (Display highscores by balance)
|
||||
* character_name_min/max_length (Minimum and maximum length of character name)
|
||||
* characters.deleted (display deleted characters on characters page)
|
||||
|
||||
### Forum:
|
||||
* show image in full screen on click
|
||||
* show user avatar (outfit) in posts
|
||||
* replaced forum actions links (move, remove, edit, quote) with images
|
||||
* redirect directly to the thread on user login (on new reply)
|
||||
|
||||
### Installer:
|
||||
* AJAX loader for the important stuff
|
||||
* create admin account: ask for e-mail + character name
|
||||
* load items & weapons
|
||||
* check user IP on install to prevent install by random user
|
||||
* remember status of the installation
|
||||
* remember language on first step (welcome)
|
||||
* ask user for timezone
|
||||
* auto detected browser language in select language
|
||||
|
||||
### Plugins
|
||||
* sandbox for plugins, don't install when requirements are not satisfied
|
||||
* allow comments inside plugin json file (php style)
|
||||
* new require options for plugins: (look into example.json)
|
||||
* require database version, table or column of the MyAAC schema
|
||||
* require php-extension
|
||||
* require semantic-version (like in composer.json)
|
||||
* new hooks: LOGIN, LOGIN_ATTEMPT, LOGOUT, HOOK_ACCOUNT_CREATE_*
|
||||
|
||||
### Cache
|
||||
* php 7.x APCu cache support (faster cache engine)
|
||||
* new cache engine: plain PHP (is good with pure php 7.0+ and opcache)
|
||||
* cache lastkills.php, $db->hasTable, $db->hasColumn, hooks and template menus
|
||||
* stop using global $cache variable, use Singleton pattern instead
|
||||
|
||||
### Twig
|
||||
* move pages to Twig templates: team, lastkills, serverinfo, houses, guilds.list, guild.view, admin.logs, admin.reports (@whiteblXK, @tobi132)
|
||||
* replace "$twig->render()" with "$this->display"
|
||||
* move Twig functions to separate file
|
||||
* move tibiacom boxes to Twig templates
|
||||
* allow Pages to be loaded as Twig template (this allows using Twig variables in Pages) (@tobi132)
|
||||
* allow string to be passed to hook twig function
|
||||
|
||||
### Functions
|
||||
* config($key), configLua($key)
|
||||
* clearCache()
|
||||
* OTS_Account:
|
||||
* getCountry()
|
||||
* setLastLogin($lastlogin) (@Leesneaks)
|
||||
* setWebFlags(webflags) (@Leesneaks)
|
||||
* OTS_Player:
|
||||
* getAccountId()
|
||||
* countBlessings() (@Leesneaks)
|
||||
* checkBlessings($count) (@Leesneaks)
|
||||
* is_sub_dir (in system/libs/plugins.php)
|
||||
* Twig:
|
||||
* getPlayerLink($name, $generate = true)
|
||||
* removed SQLquote and SQLquery from OTS_Base_DB
|
||||
* Add optional $params param into log_append (will log arrays) (@tobi132)
|
||||
|
||||
### Internal
|
||||
* moved clients list to the new file (clients.conf.php)
|
||||
* changed tableExist and fieldExist to $db->hasTable(table) + $db->hasColumn(table, column)
|
||||
* changed deprecated $ots->createObject() functions with their OTS_ equivalents
|
||||
* add global helper config($key) function + twig binding
|
||||
* use config() instead of global $config
|
||||
* remove unnecessary parentheses in include/require PHP functions
|
||||
* use __DIR__ instead of dirname(__FILE__) - since PHP 5.3.0
|
||||
* change intval() function to (int) casting (up to 6x faster)
|
||||
* add release.sh script (for GitHub releases)
|
||||
* use curl as alternative option for reporting install
|
||||
|
||||
### Libraries
|
||||
* updated Twig to version v1.35.0
|
||||
* updated TinyMCE to version v4.7.4
|
||||
|
||||
### Deprecations
|
||||
* change deprecated HTML <center> tag to <div style="text-align:center">
|
||||
* replace deprecated HTML <font> tag with <span>
|
||||
|
||||
## [0.7.11 - 04.05.2019]
|
||||
### Added:
|
||||
* support for some old servers, where arrays are used in config.lua
|
||||
* an additional text to the install page informing that user can reinstall MyAAC by deleting config.local.php
|
||||
|
||||
### Fixed:
|
||||
* XSS in forum show_thread
|
||||
* guilds - "Add new rank" function
|
||||
* multiple mail recipients when using admin mailer function
|
||||
* Admin Panel - MyAAC logs not shown if servers logs directory doesn't exist (#47)
|
||||
* missing prefix for cache get() and delete() functions
|
||||
* add fatal error message when myaac tables in database do not exist
|
||||
* the mystical defect where "Create Account" button was not highlighted (on the account/manage page)
|
||||
* bug where server_config table does not exist (OTHire as an example)
|
||||
* database_name in Usage_Statistics
|
||||
* forgot to open <head> in install template
|
||||
|
||||
### Changed:
|
||||
* do not display software version
|
||||
|
||||
## [0.7.10 - 03.03.2018]
|
||||
### Added:
|
||||
* new configurable: smtp_secure
|
||||
* robots.txt
|
||||
|
||||
### Fixed:
|
||||
* editing an existing page that had php enabled
|
||||
* chrome bug on save (when editing page) ERR_BLOCKED_BY_XSS_AUDITOR
|
||||
* showing IP and Port in admin panel (#44, by miqueiaspenha)
|
||||
* deleting plugin showing "You don't have rights to delete"
|
||||
* some bug with PHPMailer not finding its language file
|
||||
* default accounts.vote value
|
||||
* saving some really high long ip addresses
|
||||
|
||||
### Changed:
|
||||
* update config.highscores_ids_hidden on install when there are samples already in database
|
||||
* auto add z_polls table on install
|
||||
|
||||
### Internal:
|
||||
* changed mb_strtolower functions to strtolower()
|
||||
* added new function: $hooks->exist($type)
|
||||
|
||||
## [0.7.9 - 13.01.2018]
|
||||
* removed 6mb of trash (some useless things)
|
||||
* (fix) TFS 1.x not showing promoted vocations in highscores
|
||||
* otserv 0.6.x: fixed some warning (on the characters page) and fatal mysql error (on the mango signature)
|
||||
* fixed default stamina on otserv 0.6.x engine (and some others perhaps)
|
||||
* install: change permission check to is_writable
|
||||
* changed highscores_groups_hidden to 3 (for TFS 1.x)
|
||||
* updated background-artwork (tibiacom template) to the latest version, removed other ones
|
||||
|
||||
## [0.7.8 - 12.01.2018]
|
||||
* fixed installation error " call to undefined method OTS_DB_MySQL::hasColumn()"
|
||||
* updated tinymce to the latest (4.7.4) version
|
||||
* enabled emoticons plugin in tinymce :)
|
||||
* some security fixes
|
||||
|
||||
## [0.7.7 - 08.01.2018]
|
||||
* important fix for servers with promotion column (caused player.vocation to be resetted when saving player, for example: on change name, accept invite to guild, leave guild)
|
||||
* immediately reload config.lua when there's change in config.server_path detected
|
||||
* added new forum option: "Enable HTML" (only for moderators)
|
||||
* fixed othire default column value (#26)
|
||||
* fixed saving custom vocations in admin panel (#36)
|
||||
* fixed warning in highscores when vocation doesn't exist
|
||||
* fixed characters page - config.characters.frags "Notice: Use of undefined constant"
|
||||
* fixed getBoolean function when boolean is passed
|
||||
* fixed empty success message on leave guild
|
||||
* fixed displaying premium account days
|
||||
* function OTS_Account:getPremDays will now return -1 if there's freePremium configurable enabled on the server
|
||||
* fixed tr bgcolor in characters view (Frags) (#38)
|
||||
* fixed some warning in guild show
|
||||
* fixed PHP warning about country not existing on online and characters pages
|
||||
* fixed forum bbcode parsing
|
||||
* don't add extra <br/> to the TinyMCE news forum posts
|
||||
* (internal) using $player->getVocationName() where possible instead of older method
|
||||
|
||||
## [0.7.6 - 05.01.2017]
|
||||
* fixed othire account creating/installation
|
||||
* fixed table name players -> players_online
|
||||
* fixed unexpected error logging about email fail
|
||||
* added max_execution_time to the install finish step
|
||||
* some small fix regarding highscores vocation box
|
||||
|
||||
## [0.7.5 - 04.01.2017]
|
||||
* fixed bug on othire with config.account_premium_days
|
||||
* fixed bug on TFS 1.x when online_afk is enabled
|
||||
* warning about leaving news page with changes
|
||||
* added player status to tibiacom top 5 highscores box
|
||||
* save detected country on create account in session
|
||||
* fixed getPremDays and isPremium functions (newest 11.x engines are bugged when it comes to PACC, its not fault of MyAAC)
|
||||
* fix when there are no changelogs or highscores yet
|
||||
* small fix regarding getTopPlayers function which was ignoring $limit variable
|
||||
* fixed news adding when type != ARTICLE
|
||||
* fixed template path finding
|
||||
* fixed displaying article_text when it was empty saved
|
||||
|
||||
## [0.7.4 - 24.12.2017]
|
||||
* fixed mysql fatal error on tibiacom template - top 5 box
|
||||
* fixed displaying of level percent bar on tibian signature
|
||||
* inform user about Twig cache failure on installation, instead of http 500 error
|
||||
* when dir system/cache is not writable by the webserver, then show some nice notice to the user about it instead of http 500 error
|
||||
* remember client version select and usage stats checkbox in session on install
|
||||
* automatically update highscores_ids_hidden for users who installed myaac before (migration)
|
||||
|
||||
## [0.7.3 - 18.12.2017]
|
||||
* auto generate myaac cache & session prefix on install to be unique across installations
|
||||
* fixed hiding shop system menu on tibiacom template when disabled in config
|
||||
* prevent adding duplicated newses with installation
|
||||
* some changes to sample characters: chanced town_id to 1, posx: 1000, posy: 1000, posz: 1000 and default group_id to 1 so you can change in-game outfits and they will be used
|
||||
* added version 772 constant to install client choose (OTHire)
|
||||
* better solution for hidding samples (configurable) - highscores_ids_hidden
|
||||
* fixed account.login redirect not working on tibiacom template
|
||||
* installation: warn about wrong admin account name/id and password
|
||||
* fixed last menu closing in tibiacom template
|
||||
* updated polish locale (translation) on install
|
||||
* (internal) removed some duplicated code on install finish
|
||||
* (internal) renamed installation step files to be in correct order
|
||||
* added TODO file
|
||||
|
||||
## [0.7.1 - 13.12.2017]
|
||||
* added changelog menu item to kathrine template
|
||||
* fixed some php short tag in changelogs page
|
||||
* fixed guild change description back button
|
||||
* removed duplicated "Support List" menu item from tibiacom template
|
||||
* changed some notice when version check is failed
|
||||
* (internal) moved changelog to twig
|
||||
|
||||
## [0.7.0 - 20.11.2017]
|
||||
* moved template menus to database, they're now dynamically loaded
|
||||
* added anonymous usage statistics reporting (only if user agrees, first usage report will be send after 7 days)
|
||||
* you can edit them in Admin Panel under 'Menus' option
|
||||
* you can also add custom links, like http://google.pl
|
||||
* added networks (facebook and twitter) and highscores (top 5) boxes to tibiacom template, configurable in templates/tibiacom/config.php
|
||||
* added news ticker for kathrine template
|
||||
* added featured article to tibiacom template (you can add them with add news button)
|
||||
* added tinymce editor to 'Pages' in admin panel
|
||||
* added links to edit/delete/hide custom page directly from page
|
||||
* update forum post after editing news (when forum post has been created)
|
||||
* enabled code plugin for tinymce which enabled raw html code editing
|
||||
* removed videos pages, as it can be easily added using custom Menus and Pages with insert Media
|
||||
* removed bug_report configurable, its now enabled by default
|
||||
* log some error info when mail cannot be send on account create
|
||||
* twig getLink function will now return with full url (BASE_URL included)
|
||||
* verify install post values directly on config page and display error
|
||||
* updated tinymce to version 4.7.2 (from 4.7.0)
|
||||
* updated phpmailer to version 5.2.26 (from 5.2.23)
|
||||
* (#30) (fix) recovering account on servers that doesn't support salts
|
||||
* (fix) account email confirm function
|
||||
* (fix) showing changelog with urls in Admin Panel
|
||||
* (fix) uninstalling plugin
|
||||
* (fix) polls box in tibiacom template
|
||||
* (fix) remove hooks from db on plugin deinstall
|
||||
* (fix) some weird include possibilities with forum and account actions (verify action name)
|
||||
* (fix) loading hooks from plugin installed from command line
|
||||
* (fix) some changelog PHP Notice warning
|
||||
* (internal) moved uninstall logic to Plugins class
|
||||
* (internal) moved tibiacom boxes to separate directory
|
||||
* (internal) moved news tickers to twig template
|
||||
* (internal) moved Forum class to separate file
|
||||
* (internal) moved deprecated functions to compat.php
|
||||
* (internal) added some compat functions that are used by shop system
|
||||
* (internal) renamed constant TICKET -> TICKER
|
||||
* (internal) shortened message functions
|
||||
|
||||
## [0.6.6 - 22.10.2017]
|
||||
* fixed some php fatal error on spells page
|
||||
* changed spells.vocations field in db size to 300
|
||||
* please reload your spells after this update!
|
||||
|
||||
## [0.6.5 - 21.10.2017]
|
||||
* fixed displaying custom pages
|
||||
* fixed adding new group forum board
|
||||
|
||||
## [0.6.4 - 20.10.2017]
|
||||
* reverted OTS_Account::getLastLogin() cause its used by tibia11-login plugin
|
||||
|
||||
## [0.6.3 - 20.10.2017]
|
||||
* fixed creating account
|
||||
* fixed viewing thread without being logged
|
||||
* fixed showing premium account status
|
||||
|
||||
## [0.6.2 - 20.10.2017]
|
||||
* added forums for guilds and groups
|
||||
* added nice looking menu for my account page in default template
|
||||
* new command line tool: install_plugin.php - can be used to install plugins from command line. Usage: "php install_plugin.php path_to_file"
|
||||
* added new tooltip to view characters equipment item name and monster loot
|
||||
* added items.xml loader class and weapons.xml loader class
|
||||
* minimum PHP version to install AAC is now 5.3.0 cause of Anonymous functions used by Twig
|
||||
* Added 'Are you sure?' popup when uninstalling plugin
|
||||
* added some warnings when plugin json file is incomplete
|
||||
* fixed showing in characters ban expires when is unlimited
|
||||
* fixed displaying monster loot when item.name in loot is used instead of item.id
|
||||
* load also runes into spells table
|
||||
* display plugin uninstall option only if its possible
|
||||
* after changing template you will be redirected to latest viewed page
|
||||
* display gallery add image form only on main gallery page
|
||||
* (internal) moved most of guilds html-in-php code to twig
|
||||
* (internal) moved spells page to twig template
|
||||
* (internal) removed useless spells.spell column that was duplicate of spells.words
|
||||
* (internal) save monster loot in database in json format instead loading it every time from xml file
|
||||
* (internal) store monster voices and immunities in json format
|
||||
* (internal) moved buttons to separate template
|
||||
* (internal) moved online search form to twig
|
||||
* (internal) added new function getItemNameById($id)
|
||||
* (internal) Moved plugin install logic to a new class: Plugins
|
||||
* (internal) changed spells.vocations database field to store json data instead of comma separated
|
||||
* (internal) removed $hook_types array, using defined() and constant() functions now
|
||||
* (internal) removed useless monsters.gfx_name field from database
|
||||
* (internal) renamed database field monsters.hide_creature to hidden
|
||||
* (internal) renamed existing Items class to Items_Images
|
||||
* (internal) optimized Spells class
|
||||
* (internal) new function: OTS_Guild::hasMember(OTS_Player $player)
|
||||
* (internal) new function: Forum::hasAccess($board_id)
|
||||
|
||||
## [0.6.1 - 17.10.2017]
|
||||
* fixed signatures loading
|
||||
* new configurable: session_prefix, to allow more websites on one machine (must be unique for every website on your dedicated server!)
|
||||
* better error handling for monsters and spells loader (save errors to system/logs/error.log)
|
||||
* check if file exist before loading (monsters and spells)
|
||||
* (internal) Account::getAccess() = Account::getGroupId()
|
||||
* (internal) moved account actions (pages) to account/ directory
|
||||
* (internal) moved forum actions (pages) to forum/ directory
|
||||
* (internal) moved forum.edit_post to twig templates
|
||||
|
||||
## [0.6.0 - 16.10.2017]
|
||||
* added faq management - add/edit/move/hide/delete from website
|
||||
* new account.login view for tibiacom template
|
||||
* monsters and spells are now being loaded at the installation of the AAC
|
||||
* fix for php versions under 5.5 where empty() function supported only variables
|
||||
* added missing change email and change info buttons to account.management default template
|
||||
* added new indicator icons for create account, create character and change character name
|
||||
* fixed config loader when some inline comments are present
|
||||
* fixed editing page in admin panel that contains some html code
|
||||
* fixed forum new post on mac os and some specific mysql versions
|
||||
* attempt to fix incorrect views counter behavior (its resetting to 0 in some cases)
|
||||
* enabled cache http headers for signatures
|
||||
* check if monster file exist before loading it
|
||||
* fixed if plugin zip file name contains dot (.)
|
||||
* renamed screenshots to gallery and movies to videos
|
||||
* moved install pages to twig
|
||||
* fixed Account::getGuildAccess function
|
||||
* removed never used library from sources - dwoo
|
||||
* moved check_* functions to class Validator
|
||||
* from now all validators ajax requests will fire onblur instead of onkeyup
|
||||
* ajax requests returns now json instead of xml
|
||||
* added 404 response when file is not found
|
||||
|
||||
## [0.5.1 - 11.10.2017]
|
||||
* fixed forum add/edit board
|
||||
* new configurable: highscores_length, how much highscores to display
|
||||
* fixed highscores links (ALL, previous and next page)
|
||||
* update templates cache when installing/uninstalling plugin
|
||||
* moved character deaths and frags table generation to twig
|
||||
* fixed some bug when you uninstall plugin and then try to install again on the same page
|
||||
* check if plugin exist before uninstalling
|
||||
* fixed some warning in OTS_Base_DB
|
||||
|
||||
## [0.5.0 - 10.10.2017]
|
||||
* moved .htaccess rules to plain php (index.php)
|
||||
* updated tinymce to the latest (4.7.0) version, you can now embed code, for example youtube videos
|
||||
* added option to uninstall plugin
|
||||
* added option to require specified myaac, php or database version for plugins, without that plugin won't be installed
|
||||
* change accountmanagement links to use friendly_urls
|
||||
* fixed creating new forum thread
|
||||
* sample characters are now assigned to admin account and have group_id 4 to not be shown on highscores
|
||||
* added links loaded from database to admin panel - for future plugins
|
||||
* print some info to error.log when can't find config.lua
|
||||
* some fixes in account changecomment action
|
||||
* show info when account name/number or password is empty on login
|
||||
* fixed showing account login errors
|
||||
* added few characters hooks
|
||||
* fixed some kathrine template js bug when shop is disabled
|
||||
* you can now use slash '/' in custom pages loaded from database
|
||||
* added new twig function getLink that convert link taking into account config.friendly_urls
|
||||
* internalLayoutLink -> getLink
|
||||
|
||||
## [0.4.3 - 05.10.2017]
|
||||
* better config loader taken from latest gesior, you can now include files in your config by doing dofile('config.local.lua')
|
||||
* fixed country detection in create account
|
||||
* fixed showing of character deaths and frags
|
||||
* fixed https://otland.net/threads/myaac-v0-0-1.251454/page-13#post-2466303
|
||||
* fixed https://otland.net/threads/myaac-v0-0-1.251454/page-13#post-2466313
|
||||
* fixed rook sample, which will now have level 1, 150 health, 0 mana, and 400 cap.
|
||||
* fixed samples being deleted by tfs 1.0+ cause of 'deletion' field set to 1
|
||||
* pages loaded from database have higher priority than normal .php pages, so they will be loaded first if they exist
|
||||
* moved many pages to twig templates
|
||||
* change download client links from clients.halfaway.net to tibia-clients.com
|
||||
* added bugtracker to kathrine template
|
||||
* added CREDITS file
|
||||
|
||||
## [0.4.2 - 14.09.2017]
|
||||
* updated version number
|
||||
|
||||
## [0.4.1 - 13.09.2017]
|
||||
* fixed log in to admin panel
|
||||
* fixed File is not .zip plugin upload error
|
||||
|
||||
## [0.4.0 - 13.09.2017
|
||||
* added option to add/edit/delete/hide/move forum boards
|
||||
* moved some of HTML-in-PHP code to Twig templates
|
||||
* added bug_report configurable which can enable/disable bug tracker
|
||||
* log errors instead of showing them to users with system directories
|
||||
* fix when $_SERVER['HTTP_ACCEPT_ENCODING'] is not set
|
||||
* when it fails to load config.lua it will output error also to error.log
|
||||
* automatically detect json file in .zip instead of basing on filename (admin panel - plugins)
|
||||
* hopefully fixed the error with "The file you are trying to upload is not a .zip file. Please try again."
|
||||
* fixed wrong name of table in bugtracker
|
||||
* fixed some bugs in bugtracker
|
||||
* added report bug link in templates
|
||||
* fixed some rare error when user is logged in for longer than 15 minutes and tries to login again
|
||||
* fixed some grammar errors
|
||||
* some small improvements
|
||||
* fixed some separators in kathrine template
|
||||
|
||||
## [0.3.0 - 28.08.2017]
|
||||
* added administration panel for screenshots management with auto thumbnail generator and image auto-resizing
|
||||
* added Twig template engine and moved some html-in-php code to it
|
||||
* automatically detect player country based on user location (IP) on create account
|
||||
* player sex (gender) is now configurable at $config['genders']
|
||||
* fixed recovering account and changing password when salt is enabled
|
||||
* fixed installing samples when for example Rook Sample already exist and other samples not
|
||||
* fixed some mysql error when character you trying to create already exist
|
||||
* fixed some warning when you select nonexistent country
|
||||
* password change minimal/maximal length notice is now more precise
|
||||
* added 'enabled' field in myaac_hooks table, which can enable or disable specified hook
|
||||
* removed DEFAULT '' for TEXT field. It didn't worked under some systems like MAC OS X.
|
||||
* minimum PHP version to install the MyAAC is now 5.2.0 cause of pathinfo (extension) function
|
||||
* removed unused admin stylish template
|
||||
* removed some unused cities field from myaac_spells table
|
||||
* moved news adding at installation from schema.sql to finish.php
|
||||
* some optimizations
|
||||
|
||||
## [0.2.4 - 09.06.2017]
|
||||
* fixed invite to guild
|
||||
* added id field on monsters, so you can delete them in phpmyadmin
|
||||
* fixed adding some creatures with ' and "
|
||||
* fixed when there are spaces at beginning of the file (creatures)
|
||||
* fixed when file is unable to parse (creatures)
|
||||
* fixed typo loss_items => loss_containers
|
||||
* more elegant way of showing message on reload creatures and spells
|
||||
|
||||
## [0.2.3 - 31.05.2017]
|
||||
* fixed guild management on OTHire 0.0.3
|
||||
* set default skills to 10 when creating new character
|
||||
* fixed displaying of "Create forum thread" in newses
|
||||
* fixed deleting guild on servers that use players.rank_id field
|
||||
* fixed phpmailer class loading (https://otland.net/threads/myaac-v0-0-1.251454/page-8#post-2445222)
|
||||
* fixed displaying vocation amount on online page
|
||||
* better support for custom vocations, you just need to set in config vocations_amount to yours.
|
||||
* fixed huge space in player name (https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2444328)
|
||||
* fixed Undefined variable (https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2444034)
|
||||
* fixed Undefined offset (https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2444035)
|
||||
|
||||
## [0.2.2 - 22.05.2017]
|
||||
* added missing cache/signature directory
|
||||
* fixed https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2443868
|
||||
|
||||
## [0.2.1 - 21.05.2017]
|
||||
* added Swedish translation by Sizaro
|
||||
* fixed some bugs with installlation & characters & houses
|
||||
|
||||
## [0.2.0 - 21.05.2017]
|
||||
* added option to change character sex for premium points
|
||||
* moved site_closed to database, now you can close your site through admin panel
|
||||
* added option to admin panel: clear cache
|
||||
* added experiencetable_rows configurable
|
||||
* optimized OTS_Account->getGroupId(), now its using like 20 queries less
|
||||
* optimized OTS_Player->load($id) function, should be much faster now
|
||||
* fixed displaying on highscores special outfits
|
||||
* fixed skull images displaying
|
||||
* fixed displaying unlimited premium account
|
||||
* fixed bug where players.lookaddons doesn't exist (OTHire etc.) (https://otland.net/threads/myaac-v0-0-1.251454/page-6#post-2442407)
|
||||
* fixed signature tibian for OTHire and other servers that doesnt use accounts.premdays field
|
||||
* fixed when player name in signature containst space
|
||||
* don't show "Create forum thread" when editing
|
||||
* fixed red color table after create account
|
||||
* updated download links, as clients.halfaway.net isn't working anymore
|
||||
* fixed some bugs while installing when field `email_next` or `hidden` already exist
|
||||
* fixed movies unexpected comment
|
||||
* added template_place_holder('center_top') to kathrine template
|
||||
|
||||
## [0.1.5 - 13.05.2017]
|
||||
* fixed bug with "Integrity constraint violation: 1048 Column 'ip' cannot be null"
|
||||
|
||||
## [0.1.4 - 13.05.2017]
|
||||
* added outfit shower, in characters, online, and highscores
|
||||
* updated database to version 2
|
||||
* fixed item images (now using item-images.ots.me host by default)
|
||||
* fixed news ticket and posting long newses (https://otland.net/threads/myaac-v0-0-1.251454/page-5#post-2442026)
|
||||
* news body limit increased to 65535 (mysql text field)
|
||||
* removed some unused code from my old server
|
||||
* added spells & monsters to kathrine template
|
||||
|
||||
## [0.1.3 - 11.05.2017]
|
||||
* this is just release to update version number
|
||||
|
||||
## [0.1.2 - 11.05.2017]
|
||||
* forgot to update CHANGELOG and MYAAC_VERSION
|
||||
|
||||
## [0.1.1 - 11.05.2017]
|
||||
* fixed updating myaac_config with database_version to 1
|
||||
* fixed database updater
|
||||
|
||||
## [0.1.0 - 11.05.2017]
|
||||
* added new feature: change character name for premium points (disabled by default, you can enable it in config under account_change_character_name in config.php)
|
||||
* added automatic database updater (data migrations)
|
||||
* renamed events to hooks
|
||||
* moved hooks to database
|
||||
* now you can use hooks in plugins
|
||||
* set account.type field to 5 on install, if TFS 1.0+
|
||||
* added example plugin
|
||||
* new, latest google analytics code
|
||||
* fixed bug with loading account.name that has numbers in it
|
||||
* fixed many bugs in player editor in admin panel
|
||||
* added error handling to plugin manager and some more verification in
|
||||
* file has been correctly unpacked/uploaded
|
||||
* fixed Statistics page in admin panel when using account.number
|
||||
* fixed bug when creating/recovering account on servers with
|
||||
* account.salt field (TFS 0.3 for example)
|
||||
* fixed forum showing thread with html tags (added from news manager)
|
||||
* new, latest code for youtube videos in movies page
|
||||
* fixed showing vocation images when using $config['online_vocations_images']
|
||||
* many fixes in polls (also importing proper schema)
|
||||
* fixed hovering on buttons in kathrine template (on accountmanagement page)
|
||||
* fixed signatures (many fixes)
|
||||
* added missing gesior signature system
|
||||
|
||||
## [0.0.6 - 06.05.2017]
|
||||
* fixed bug while installing (https://otland.net/threads/myaac-v0-0-1.251454/page-3#post-2440543)
|
||||
* fixed bug when creating character (not showing errors) (one more time)
|
||||
* fixed support for TFS 0.2 series
|
||||
* added FAQ link
|
||||
|
||||
## [0.0.5 - 05.05.2017]
|
||||
* fixed bug when creating character (not showing errors)
|
||||
* Fixed characters loading with names that has been created with other AAC
|
||||
* fixed links to shop in default template
|
||||
* fixed some weird PHP 7.1 warnings/notices
|
||||
* Fixed config loading with some weird comments
|
||||
* fixed bug with status info utf8 encoding (https://otland.net/threads/myaac-v0-0-1.251454/page-2#post-2440259)
|
||||
* fixed when ip in log_action is NULL (https://otland.net/threads/myaac-v0-0-1.251454/page-2#post-2440357)
|
||||
* fixed bug when guild doesn't exist on characters page (https://otland.net/threads/myaac-v0-0-1.251454/page-2#post-2440320)
|
||||
* disabled friendly_urls by default
|
||||
* fixes when $config['database_*'] is set
|
||||
* added CHANGELOG
|
||||
|
||||
## [0.0.3 - 03.05.2017]
|
||||
* Full support for OTHire 0.0.3
|
||||
* added support for otservers that doesn't use account.name field, instead just account number will be used
|
||||
* fixed encryption detection on TFS 0.3
|
||||
* fixed bug when server_config table doesn't exist
|
||||
* (install) moved admin account creation to new step
|
||||
* fixed news comment link
|
||||
* by default, the installer creates now the Admin player, for admin account
|
||||
* fixed installation errors
|
||||
* fixed config.lua loading with some weird comments
|
||||
|
||||
## [0.0.2 - 02.05.2017]
|
||||
* updated forum links to use friendly_urls
|
||||
* some more info will be shown when cannot connect to database
|
||||
* show more error infos when creating character
|
||||
* fixed forum link on newses
|
||||
* fixed spells loading when there's vocation name instead of id
|
||||
* fixed bug when you have changed template but it doesn't exist anymore
|
||||
* fixed vocations with promotion loading
|
||||
* fixed support for gesior pages and templates
|
||||
* added function OTS_Acount:getGroupId()
|
||||
|
||||
## [0.0.1 - 01.05.2017]
|
||||
This is first official release of MyAAC.
|
||||
Features are listed here
|
||||
|
||||
For more information, see the release announcement on OTLand: https://otland.net/threads/myaac-v0-0-1.251454/
|
||||
### Fixed
|
12
README.md
@ -1,11 +1,19 @@
|
||||
# myaac
|
||||
# MyAAC
|
||||
|
||||
[](https://travis-ci.org/github/slawkens/myaac)
|
||||
[](https://opensource.org/licenses/gpl-license)
|
||||
[](https://github.com/slawkens/myaac/releases)
|
||||
[](https://github.com/slawkens/myaac/blob/d8b3b4135827ee17e3c6d41f08a925e718c587ed/.travis.yml#L3)
|
||||
[](https://discord.gg/2J39Wus)
|
||||
[](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed)
|
||||
|
||||
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
|
||||
|
||||
### REQUIREMENTS
|
||||
|
||||
- PHP 5.5 or later
|
||||
- PHP 5.6 or later
|
||||
- MySQL database
|
||||
- PDO PHP Extension
|
||||
- XML PHP Extension
|
||||
|
@ -2,6 +2,9 @@
|
||||
// few things we'll need
|
||||
require '../common.php';
|
||||
|
||||
define('ADMIN_PANEL', true);
|
||||
define('MYAAC_ADMIN', true);
|
||||
|
||||
if(file_exists(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!');
|
||||
}
|
||||
|
||||
define('ADMIN_PANEL', true);
|
||||
|
||||
$content = '';
|
||||
|
||||
// validate page
|
||||
|
48
admin/template/menus.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
['name' => 'Dashboard', 'icon' => 'tachometer-alt', 'link' => 'dashboard'],
|
||||
['name' => 'News', 'icon' => 'newspaper', 'link' =>
|
||||
[
|
||||
['name' => 'View', 'link' => 'news'],
|
||||
['name' => 'Add news', 'link' => 'news&action=new&type=1'],
|
||||
['name' => 'Add ticker', 'link' => 'news&action=new&type=2'],
|
||||
['name' => 'Add article', 'link' => 'news&action=new&type=3'],
|
||||
],
|
||||
],
|
||||
['name' => 'Changelogs', 'icon' => 'newspaper', 'link' =>
|
||||
[
|
||||
['name' => 'View', 'link' => 'changelog'],
|
||||
['name' => 'Add', 'link' => 'changelog&action=new'],
|
||||
],
|
||||
],
|
||||
['name' => 'Mailer', 'icon' => 'envelope', 'link' => 'mailer', 'disabled' => !config('mail_enabled')],
|
||||
['name' => 'Pages', 'icon' => 'book', 'link' =>
|
||||
[
|
||||
['name' => 'View', 'link' => 'pages'],
|
||||
['name' => 'Add', 'link' => 'pages&action=new'],
|
||||
],
|
||||
],
|
||||
['name' => 'Menus', 'icon' => 'list', 'link' => 'menus'],
|
||||
['name' => 'Plugins', 'icon' => 'plug', 'link' => 'plugins'],
|
||||
['name' => 'Server Data', 'icon' => 'gavel', 'link' => 'data'],
|
||||
['name' => 'Editor', 'icon' => 'edit', 'link' =>
|
||||
[
|
||||
['name' => 'Accounts', 'link' => 'accounts'],
|
||||
['name' => 'Players', 'link' => 'players'],
|
||||
],
|
||||
],
|
||||
['name' => 'Tools', 'icon' => 'tools', 'link' =>
|
||||
[
|
||||
['name' => 'Notepad', 'link' => 'notepad'],
|
||||
['name' => 'phpinfo', 'link' => 'phpinfo'],
|
||||
],
|
||||
],
|
||||
['name' => 'Logs', 'icon' => 'bug', 'link' =>
|
||||
[
|
||||
['name' => 'Logs', 'link' => 'logs'],
|
||||
['name' => 'Reports', 'link' => 'reports'],
|
||||
['name' => 'Visitors', 'icon' => 'user', 'link' => 'visitors'],
|
||||
],
|
||||
],
|
||||
];
|
@ -0,0 +1,10 @@
|
||||
.menu-text-li {color: #4b646f; background: #1a2226;}
|
||||
.menu-text {
|
||||
display: block;
|
||||
padding: .5rem 1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sidebar-mini.sidebar-collapse .menu-text {
|
||||
display: none;
|
||||
}
|
@ -3,11 +3,13 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<?php echo template_header(true); ?>
|
||||
<title><?php echo (isset($title) ? $title . $config['title_separator'] : '') . $config['lua']['serverName'];?></title>
|
||||
<title><?php echo (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];?></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/adminlte.min.css">
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/font-awesome.min.css">
|
||||
<?php if (isset($use_datatable)) { ?>
|
||||
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/datatables.bs.min.css">
|
||||
<?php } ?>
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo $template_path; ?>style.css"/>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="<?php echo BASE_URL; ?>tools/js/html5shiv.min.js"></script>
|
||||
@ -39,51 +41,30 @@
|
||||
<span class="brand-text"><b>My</b>AAC</span>
|
||||
</a>
|
||||
<div class="sidebar">
|
||||
<nav class="mt-2">
|
||||
<nav class="mt-1">
|
||||
<ul class="nav nav-pills nav-sidebar flex-column nav-legacy nav-child-indent" data-widget="treeview" data-accordion="false">
|
||||
<li class="menu-text-li">
|
||||
<span class="menu-text">
|
||||
<a class="text-info" href="<?php echo BASE_URL; ?>" target="_blank">
|
||||
<?php echo $config['lua']['serverName'] ?>
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
<?php
|
||||
// name = Display name of link
|
||||
// icon = fontawesome icon name without "fas fa-"
|
||||
// link = Page link or use as array for sub items
|
||||
$menus = [
|
||||
['name' => 'Dashboard', 'icon' => 'tachometer-alt', 'link' => 'dashboard'],
|
||||
['name' => 'News', 'icon' => 'newspaper', 'link' => 'news'],
|
||||
['name' => 'Mailer', 'icon' => 'envelope', 'link' => 'mailer'],
|
||||
['name' => 'Pages', 'icon' => 'book', 'link' =>
|
||||
[
|
||||
['name' => 'All Pages', 'link' => 'pages'],
|
||||
['name' => 'Add new', 'link' => 'pages&action=new'],
|
||||
],
|
||||
],
|
||||
['name' => 'Menus', 'icon' => 'list', 'link' => 'menus'],
|
||||
['name' => 'Plugins', 'icon' => 'plug', 'link' => 'plugins'],
|
||||
['name' => 'Visitors', 'icon' => 'user', 'link' => 'visitors'],
|
||||
['name' => 'Items', 'icon' => 'gavel', 'link' => 'items'],
|
||||
['name' => 'Editor', 'icon' => 'edit', 'link' =>
|
||||
[
|
||||
['name' => 'Accounts', 'link' => 'accounts'],
|
||||
['name' => 'Players', 'link' => 'players'],
|
||||
],
|
||||
],
|
||||
['name' => 'Tools', 'icon' => 'tools', 'link' =>
|
||||
[
|
||||
['name' => 'Notepad', 'link' => 'notepad'],
|
||||
['name' => 'phpinfo', 'link' => 'phpinfo'],
|
||||
],
|
||||
],
|
||||
['name' => 'Logs', 'icon' => 'bug', 'link' =>
|
||||
[
|
||||
['name' => 'Logs', 'link' => 'logs'],
|
||||
['name' => 'Reports', 'link' => 'reports'],
|
||||
],
|
||||
],
|
||||
];
|
||||
$menus = require __DIR__ . '/menus.php';
|
||||
|
||||
foreach ($menus as $category => $menu) {
|
||||
if (isset($menu['disabled']) && $menu['disabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$has_child = is_array($menu['link']);
|
||||
if (!$has_child) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link<?php echo($page == $menu['link'] ? ' active' : '') ?>" href="?p=<?php echo $menu['link'] ?>">
|
||||
<a class="nav-link<?php echo(strpos($menu['link'], $page) !== false ? ' active' : '') ?>" href="?p=<?php echo $menu['link'] ?>">
|
||||
<i class="nav-icon fas fa-<?php echo(isset($menu['icon']) ? $menu['icon'] : 'link') ?>"></i>
|
||||
<p><?php echo $menu['name'] ?></p>
|
||||
</a>
|
||||
@ -138,11 +119,11 @@
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0 text-dark"><?php echo(isset($title) ? $title : ''); ?><small> - Admin Panel</small></h1>
|
||||
<h3 class="m-0 text-dark"><?php echo(isset($title) ? $title : ''); ?><small> - Admin Panel</small></h3>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="float-sm-right">
|
||||
<span class="p-2 right badge badge-<?php echo(($status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
|
||||
<div class="float-sm-right d-none d-sm-inline">
|
||||
<span class="p-2 right badge badge-<?php echo((isset($status['online']) and $status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -182,15 +163,10 @@
|
||||
</aside>
|
||||
|
||||
<footer class="main-footer">
|
||||
<div class="float-right d-none d-sm-inline">
|
||||
<div id="status">
|
||||
<?php if ($status['online']): ?>
|
||||
<p class="success" style="width: 120px;">Server Online</p>
|
||||
<?php else: ?>
|
||||
<p class="error" style="width: 120px;">Server Offline</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div><?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?>
|
||||
<div class="float-sm-right d-none d-sm-inline">
|
||||
<span class="p-2 right badge badge-<?php echo((isset($status['online']) and $status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
|
||||
</div>
|
||||
<?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?>
|
||||
</footer>
|
||||
<div id="sidebar-overlay"></div>
|
||||
</div>
|
||||
@ -211,8 +187,10 @@ if ($logged && admin()) {
|
||||
?>
|
||||
<script src="<?php echo BASE_URL; ?>tools/js/bootstrap.min.js"></script>
|
||||
<script src="<?php echo BASE_URL; ?>tools/js/jquery-ui.min.js"></script>
|
||||
<?php if (isset($use_datatable)) { ?>
|
||||
<script src="<?php echo BASE_URL; ?>tools/js/datatables.min.js"></script>
|
||||
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
|
||||
<?php } ?>
|
||||
<script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
define('MYAAC_ADMIN', true);
|
||||
|
||||
require '../../common.php';
|
||||
require SYSTEM . 'functions.php';
|
||||
require SYSTEM . 'init.php';
|
||||
|
46
admin/tools/reload_data.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Project: MyAAC
|
||||
* Automatic Account Creator for Open Tibia Servers
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* @package MyAAC
|
||||
* @author Slawkens <slawkens@gmail.com>
|
||||
* @copyright 2020 MyAAC
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
define('MYAAC_ADMIN', true);
|
||||
|
||||
require '../../common.php';
|
||||
require SYSTEM . 'functions.php';
|
||||
require SYSTEM . 'init.php';
|
||||
require SYSTEM . 'login.php';
|
||||
|
||||
if (!admin())
|
||||
die('Access denied.');
|
||||
|
||||
ini_set('max_execution_time', 300);
|
||||
ob_implicit_flush();
|
||||
ob_end_flush();
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
require LIBS . 'DataLoader.php';
|
||||
|
||||
require LOCALE . 'en/main.php';
|
||||
require LOCALE . 'en/install.php';
|
||||
|
||||
DataLoader::setLocale($locale);
|
||||
DataLoader::load();
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
define('MYAAC_ADMIN', true);
|
||||
|
||||
require '../../common.php';
|
||||
require SYSTEM . 'init.php';
|
||||
require SYSTEM . 'functions.php';
|
||||
|
15
common.php
@ -23,12 +23,12 @@
|
||||
* @copyright 2019 MyAAC
|
||||
* @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.1', '<')) die('PHP version 7.1 or higher is required.');
|
||||
session_start();
|
||||
|
||||
define('MYAAC', true);
|
||||
define('MYAAC_VERSION', '0.8.2-dev');
|
||||
define('DATABASE_VERSION', 30);
|
||||
define('MYAAC_VERSION', '0.9.0-dev');
|
||||
define('DATABASE_VERSION', 32);
|
||||
define('TABLE_PREFIX', 'myaac_');
|
||||
define('START_TIME', microtime(true));
|
||||
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
|
||||
@ -67,6 +67,7 @@ define('PAGES', SYSTEM . 'pages/');
|
||||
define('PLUGINS', BASE . 'plugins/');
|
||||
define('TEMPLATES', BASE . 'templates/');
|
||||
define('TOOLS', BASE . 'tools/');
|
||||
define('VENDOR', BASE . 'vendor/');
|
||||
|
||||
// menu categories
|
||||
define('MENU_CATEGORY_NEWS', 1);
|
||||
@ -115,4 +116,10 @@ if(!IS_CLI) {
|
||||
|
||||
require SYSTEM . 'exception.php';
|
||||
}
|
||||
require SYSTEM . 'autoload.php';
|
||||
|
||||
$autoloadFile = VENDOR . 'autoload.php';
|
||||
if (!is_file($autoloadFile)) {
|
||||
throw new RuntimeException('The vendor folder is missing. Please download Composer: <a href="https://getcomposer.org/download">https://getcomposer.org/download</a>, install it and execute in the main MyAAC directory this command: <b>composer install</b>. Or download MyAAC from <a href="https://github.com/slawkens/myaac/releases">GitHub releases</a>, which includes Vendor folder.');
|
||||
}
|
||||
|
||||
require $autoloadFile;
|
||||
|
16
composer.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-pdo_mysql": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-zip": "*",
|
||||
"phpmailer/phpmailer": "^6.1",
|
||||
"composer/semver": "^3.2",
|
||||
"twig/twig": "~1.42.5",
|
||||
"erusev/parsedown": "^1.7"
|
||||
}
|
||||
}
|
41
config.php
@ -52,7 +52,6 @@ $config = array(
|
||||
// head options (html)
|
||||
'meta_description' => 'Tibia is a free massive multiplayer online role playing game (MMORPG).', // description of the site
|
||||
'meta_keywords' => 'free online game, free multiplayer game, ots, open tibia server', // keywords list separated by commas
|
||||
'title_separator' => ' - ',
|
||||
|
||||
// footer
|
||||
'footer' => ''/*'<br/>Your Server © 2016. All rights reserved.'*/,
|
||||
@ -86,8 +85,17 @@ $config = array(
|
||||
),
|
||||
|
||||
// images
|
||||
'outfit_images_url' => 'http://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
|
||||
'outfit_images_url' => 'https://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit
|
||||
'outfit_images_wrong_looktypes' => [75, 126, 127, 266, 302], // this looktypes needs to have different margin-top and margin-left because they are wrong positioned
|
||||
'item_images_url' => 'https://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
|
||||
'item_images_extension' => '.gif',
|
||||
|
||||
// creatures
|
||||
'creatures_images_url' => 'images/monsters/', // set to images/monsters if you host your own creatures in images folder
|
||||
'creatures_images_extension' => '.gif',
|
||||
'creatures_images_preview' => false, // set to true to allow picture previews for creatures
|
||||
'creatures_items_url' => 'https://tibia.fandom.com/wiki/', // set to website which shows details about items.
|
||||
'creatures_loot_percentage' => true, // set to true to show the loot tooltip percent
|
||||
|
||||
// account
|
||||
'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager)
|
||||
@ -95,6 +103,7 @@ $config = array(
|
||||
'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_unique' => true, // email addresses cannot be duplicated? (one account = one email)
|
||||
'account_mail_block_plus_sign' => true, // block email with '+' signs like test+box@gmail.com (help protect against spamming accounts)
|
||||
'account_premium_days' => 0, // default premium days on new account
|
||||
'account_premium_points' => 0, // default premium points on new account
|
||||
'account_welcome_mail' => true, // send welcome email when user registers
|
||||
@ -151,17 +160,23 @@ $config = array(
|
||||
4 => 'Knight Sample'
|
||||
),
|
||||
|
||||
// it must show limited number of players after using search in character page
|
||||
'characters_search_limit' => 15,
|
||||
|
||||
// town list used when creating character
|
||||
// won't be displayed if there is only one item (rookgaard for example)
|
||||
'character_towns' => array(1),
|
||||
|
||||
// characters lenght
|
||||
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum lenght be 21.
|
||||
// characters length
|
||||
// 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_max_length' => 21,
|
||||
'character_name_npc_check' => true,
|
||||
|
||||
// list of towns
|
||||
// if you use TFS 1.3 with support for 'towns' table in database, then you can ignore this - it will be configured automatically (generated from your .OTBM map)
|
||||
// if you use TFS 1.3 with support for 'towns' table in database, then you can ignore this - it will be configured automatically (from MySQL database - Table - towns)
|
||||
// otherwise it will try to load from your .OTBM map file
|
||||
// if you don't see towns on website, then you need to fill this out
|
||||
'towns' => array(
|
||||
0 => 'No town',
|
||||
1 => 'Sample town'
|
||||
@ -172,6 +187,7 @@ $config = array(
|
||||
'guild_need_level' => 1, // min. level to form a guild
|
||||
'guild_need_premium' => true, // require premium account to form a guild?
|
||||
'guild_image_size_kb' => 80, // maximum size of the guild logo image in KB (kilobytes)
|
||||
'guild_description_default' => 'New guild. Leader must edit this text :)',
|
||||
'guild_description_chars_limit' => 1000, // limit of guild description
|
||||
'guild_description_lines_limit' => 6, // limit of lines, if description has more lines it will be showed as long text, without 'enters'
|
||||
'guild_motd_chars_limit' => 150, // limit of MOTD (message of the day) that is shown later in the game on the guild channel
|
||||
@ -192,19 +208,19 @@ $config = array(
|
||||
'team_display_outfit' => true,
|
||||
|
||||
// bans page
|
||||
'bans_limit' => 50,
|
||||
'bans_display_all' => true, // should all bans be displayed? (sorted page by page)
|
||||
'bans_per_page' => 20,
|
||||
|
||||
// highscores page
|
||||
'highscores_vocation_box' => true, // show 'Choose a vocation' box on the highscores (allowing peoples to sort highscores by vocation)?
|
||||
'highscores_vocation' => true, // show player vocation under his nickname?
|
||||
'highscores_frags' => false, // show 'Frags' tab (best fraggers on the server)? Only 0.3
|
||||
'highscores_frags' => false, // show 'Frags' tab (best fraggers on the server)?
|
||||
'highscores_balance' => false, // show 'Balance' tab (richest players on the server)
|
||||
'highscores_outfit' => true, // show player outfit?
|
||||
'highscores_country_box' => false, // doesnt work yet! (not implemented)
|
||||
'highscores_groups_hidden' => 3, // this group id and higher won't be shown on the highscores
|
||||
'highscores_ids_hidden' => array(0), // this ids of players will be hidden on the highscores (should be ids of samples)
|
||||
'highscores_length' => 100, // how many records per page on highscores
|
||||
'highscores_per_page' => 100, // how many records per page on highscores
|
||||
'highscores_cache_ttl' => 15, // how often to update highscores from database in minutes (default 15 minutes)
|
||||
|
||||
// characters page
|
||||
'characters' => array( // what things to display on character view page (true/false in each option)
|
||||
@ -255,9 +271,10 @@ $config = array(
|
||||
'last_kills_limit' => 50, // max. number of deaths shown on the last kills page
|
||||
|
||||
// status, took automatically from config file if empty
|
||||
'status_enabled' => true, // you can disable status checking by settings this to "false"
|
||||
'status_ip' => '',
|
||||
'status_port' => '',
|
||||
'status_timeout' => 2, // how long to wait for the initial response from the server (default: 2 seconds)
|
||||
'status_timeout' => 2.0, // how long to wait for the initial response from the server (default: 2 seconds)
|
||||
|
||||
// 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
|
||||
@ -265,7 +282,7 @@ $config = array(
|
||||
'status_interval' => 60,
|
||||
|
||||
// admin panel
|
||||
'admin_panel_modules' => 'lastlogin,points,coins',
|
||||
'admin_panel_modules' => 'statistics,web_status,server_status,lastlogin,created,points,coins,balance', // default - statistics,web_status,server_status,lastlogin,created,points,coins,balance
|
||||
|
||||
// other
|
||||
'anonymous_usage_statistics' => true,
|
||||
|
BIN
images/del.png
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 318 B |
BIN
images/druid.png
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 10 KiB |
BIN
images/edit.png
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 363 B |
BIN
images/error.png
Before Width: | Height: | Size: 706 B After Width: | Height: | Size: 592 B |
BIN
images/false.png
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 845 B |
BIN
images/hist.png
Before Width: | Height: | Size: 117 B After Width: | Height: | Size: 110 B |
BIN
images/info.png
Before Width: | Height: | Size: 783 B After Width: | Height: | Size: 631 B |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 1005 B |
Before Width: | Height: | Size: 789 B After Width: | Height: | Size: 735 B |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 8.2 KiB |
BIN
images/plus.png
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 615 B After Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 816 B After Width: | Height: | Size: 633 B |
BIN
images/trash.png
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 474 B |
BIN
images/true.png
Before Width: | Height: | Size: 809 B After Width: | Height: | Size: 709 B |
20
index.php
@ -56,11 +56,17 @@ if(file_exists(BASE . 'config.local.php')) {
|
||||
require_once BASE . 'config.local.php';
|
||||
}
|
||||
|
||||
ini_set('log_errors', 1);
|
||||
if(config('env') === 'dev') {
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
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'))
|
||||
{
|
||||
@ -97,10 +103,12 @@ else {
|
||||
'/^account\/character\/comment\/[A-Za-z0-9-_%+\']+\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_comment', 'name' => '$3'),
|
||||
'/^account\/character\/comment\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_comment'),
|
||||
'/^account\/confirm_email\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'confirm_email', 'v' => '$2'),
|
||||
'/^bans\/[0-9]+\/?$/' => array('subtopic' => 'bans', 'page' => '$1'),
|
||||
'/^characters\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'characters', 'name' => '$1'),
|
||||
'/^changelog\/[0-9]+\/?$/' => array('subtopic' => 'changelog', 'page' => '$1'),
|
||||
'/^commands\/add\/?$/' => array('subtopic' => 'commands', 'action' => 'add'),
|
||||
'/^commands\/edit\/?$/' => array('subtopic' => 'commands', 'action' => 'edit'),
|
||||
'/^creatures\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'creatures', 'creature' => '$1'),
|
||||
'/^faq\/add\/?$/' => array('subtopic' => 'faq', 'action' => 'add'),
|
||||
'/^faq\/edit\/?$/' => array('subtopic' => 'faq', 'action' => 'edit'),
|
||||
'/^forum\/add_board\/?$/' => array('subtopic' => 'forum', 'action' => 'add_board'),#
|
||||
@ -327,11 +335,15 @@ if($load_it)
|
||||
)) . $content;
|
||||
}
|
||||
} else {
|
||||
$file = SYSTEM . 'pages/' . $page . '.php';
|
||||
$file = $template_path . '/pages/' . $page . '.php';
|
||||
if(!@file_exists($file))
|
||||
{
|
||||
$page = '404';
|
||||
$file = SYSTEM . 'pages/404.php';
|
||||
$file = SYSTEM . 'pages/' . $page . '.php';
|
||||
if(!@file_exists($file))
|
||||
{
|
||||
$page = '404';
|
||||
$file = SYSTEM . 'pages/404.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,7 +377,7 @@ if ($logged && admin()) {
|
||||
'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId()
|
||||
]);
|
||||
}
|
||||
$title_full = (isset($title) ? $title . $config['title_separator'] : '') . $config['lua']['serverName'];
|
||||
$title_full = (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];
|
||||
require $template_path . '/' . $template_index;
|
||||
|
||||
echo base64_decode('PCEtLSBQb3dlcmVkIGJ5IE15QUFDIDo6IGh0dHBzOi8vd3d3Lm15LWFhYy5vcmcvIC0tPg==') . PHP_EOL;
|
||||
|
@ -6,12 +6,18 @@ $ots = POT::getInstance();
|
||||
require SYSTEM . 'database.php';
|
||||
|
||||
if(!isset($db)) {
|
||||
$database_error = $locale['step_database_error_mysql_connect'] . '<br/>' .
|
||||
$locale['step_database_error_mysql_connect_2'] .
|
||||
'<ul>' .
|
||||
'<li>' . $locale['step_database_error_mysql_connect_3'] . '</li>' .
|
||||
'<li>' . $locale['step_database_error_mysql_connect_4'] . '</li>' .
|
||||
'</ul>' . '<br/>' . $error;
|
||||
$database_error = '<p class="lead">' . $locale['step_database_error_mysql_connect'] . '</p>';
|
||||
|
||||
$database_error .= '<p>' . $locale['step_database_error_mysql_connect_2'] . '</p>';
|
||||
|
||||
$database_error .= '<ul class="list-group">' .
|
||||
'<li class="list-group-item list-group-item-warning">' . $locale['step_database_error_mysql_connect_3'] . '</li>' .
|
||||
'<li class="list-group-item list-group-item-warning">' . $locale['step_database_error_mysql_connect_4'] . '</li>' .
|
||||
'</ul>';
|
||||
|
||||
$database_error .= '<div class="alert alert-danger mt-4">
|
||||
<span>' . $error . '</span>
|
||||
</div>';
|
||||
}
|
||||
else {
|
||||
if($db->hasTable('accounts'))
|
||||
|
@ -62,9 +62,9 @@ function next_buttons($previous = true, $next = true)
|
||||
$ret .= '<input class="button" type="submit" onclick="document.getElementById(\'step\').value=\'' . $steps[$i + 1] . '\';" value="' . $locale['next'] . '" />';
|
||||
*/
|
||||
if($previous)
|
||||
$ret .= '<input type="button" class="button" onclick="document.getElementById(\'step\').value=\'' . $steps[$i - 1] . '\'; this.form.submit();" value="« ' . $locale['previous'] . '" />';
|
||||
$ret .= '<input type="button" class="button btn btn-primary m-2" onclick="document.getElementById(\'step\').value=\'' . $steps[$i - 1] . '\'; this.form.submit();" value="« ' . $locale['previous'] . '" />';
|
||||
if($next)
|
||||
$ret .= '<input type="button" class="button" onclick="document.getElementById(\'step\').value=\'' . $steps[$i + 1] . '\'; this.form.submit(); " value="' . $locale['next'] . ' »" />';
|
||||
$ret .= '<input type="button" class="button btn btn-primary m-2" onclick="document.getElementById(\'step\').value=\'' . $steps[$i + 1] . '\'; this.form.submit(); " value="' . $locale['next'] . ' »" />';
|
||||
|
||||
$ret .= '</div>';
|
||||
return $ret;
|
||||
|
@ -1,3 +1,5 @@
|
||||
SET @myaac_database_version = 32;
|
||||
|
||||
CREATE TABLE `myaac_account_actions`
|
||||
(
|
||||
`account_id` INT(11) NOT NULL,
|
||||
@ -57,6 +59,8 @@ CREATE TABLE `myaac_config`
|
||||
UNIQUE (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
|
||||
|
||||
INSERT INTO `myaac_config` (`name`, `value`) VALUES ('database_version', @myaac_database_version);
|
||||
|
||||
CREATE TABLE `myaac_faq`
|
||||
(
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
@ -203,21 +207,24 @@ CREATE TABLE `myaac_monsters` (
|
||||
`use_haste` tinyint(1) NOT NULL,
|
||||
`voices` text NOT NULL,
|
||||
`immunities` varchar(255) NOT NULL,
|
||||
`elements` TEXT NOT NULL,
|
||||
`summonable` tinyint(1) NOT NULL,
|
||||
`convinceable` tinyint(1) NOT NULL,
|
||||
`pushable` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`canpushitems` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`canwalkonenergy` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`canwalkonpoison` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`canwalkonfire` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`runonhealth` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`hostile` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`attackable` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`rewardboss` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`defense` INT(11) NOT NULL DEFAULT '0',
|
||||
`armor` INT(11) NOT NULL DEFAULT '0',
|
||||
`canpushcreatures` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`race` varchar(255) NOT NULL,
|
||||
`loot` text NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
|
||||
|
||||
CREATE TABLE `myaac_videos`
|
||||
(
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`youtube_id` VARCHAR(20) NOT NULL,
|
||||
`author` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`ordering` INT(11) NOT NULL DEFAULT 0,
|
||||
`hidden` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`summons` TEXT NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
|
||||
|
||||
@ -322,7 +329,7 @@ CREATE TABLE `myaac_visitors`
|
||||
(
|
||||
`ip` VARCHAR(16) NOT NULL,
|
||||
`lastvisit` INT(11) NOT NULL DEFAULT 0,
|
||||
`page` VARCHAR(100) NOT NULL,
|
||||
`page` VARCHAR(2048) NOT NULL,
|
||||
UNIQUE (`ip`)
|
||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
|
||||
|
||||
|
@ -95,10 +95,6 @@ if($step == 'database') {
|
||||
$errors[] = $locale['step_config_mail_admin_error'];
|
||||
break;
|
||||
}
|
||||
else if($key == 'mail_address' && !Validator::email($value)) {
|
||||
$errors[] = $locale['step_config_mail_address_error'];
|
||||
break;
|
||||
}
|
||||
else if($key == 'timezone' && !in_array($value, DateTimeZone::listIdentifiers())) {
|
||||
$errors[] = $locale['step_config_timezone_error'];
|
||||
break;
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) {
|
||||
echo '<p class="warning">' . $locale['already_installed'] . '</p>';
|
||||
echo '<div class="alert alert-warning"><span>' . $locale['already_installed'] . '</span></div>';
|
||||
}
|
||||
else {
|
||||
unset($_SESSION['saved']);
|
||||
|
@ -1,6 +1,22 @@
|
||||
<?php
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
// configuration
|
||||
$dirs_required = [
|
||||
'system/logs',
|
||||
'system/cache',
|
||||
];
|
||||
$dirs_optional = [
|
||||
'images/guilds' => $locale['step_requirements_warning_images_guilds'],
|
||||
'images/gallery' => $locale['step_requirements_warning_images_gallery'],
|
||||
];
|
||||
|
||||
$extensions_required = [
|
||||
'json', 'pdo', 'pdo_mysql', 'xml', 'zip'
|
||||
];
|
||||
$extensions_optional = [
|
||||
'gd' => $locale['step_requirements_warning_player_signatures'],
|
||||
];
|
||||
/*
|
||||
*
|
||||
* @param string $name
|
||||
@ -10,11 +26,11 @@ defined('MYAAC') or die('Direct access not allowed!');
|
||||
function version_check($name, $ok, $info = '', $warning = false)
|
||||
{
|
||||
global $failed;
|
||||
echo '<p class="' . ($ok ? 'success' : ($warning ? 'warning' : 'error')) . '">' . $name;
|
||||
echo '<div class="alert alert-' . ($ok ? 'success' : ($warning ? 'warning' : 'danger')) . '">' . $name;
|
||||
if(!empty($info))
|
||||
echo ': <b>' . $info . '</b>';
|
||||
|
||||
echo '</p>';
|
||||
echo '</div>';
|
||||
if(!$ok && !$warning)
|
||||
$failed = true;
|
||||
}
|
||||
@ -23,27 +39,42 @@ $failed = false;
|
||||
|
||||
// start validating
|
||||
version_check($locale['step_requirements_php_version'], (PHP_VERSION_ID >= 50500), PHP_VERSION);
|
||||
foreach(array('images/guilds', 'images/houses', 'images/gallery') as $value)
|
||||
|
||||
foreach ($dirs_required as $value)
|
||||
{
|
||||
$is_writable = is_writable(BASE . $value);
|
||||
$is_writable = is_writable(BASE . $value) && (MYAAC_OS != 'WINDOWS' || win_is_writable(BASE . $value));
|
||||
version_check($locale['step_requirements_write_perms'] . ': ' . $value, $is_writable);
|
||||
}
|
||||
|
||||
foreach ($dirs_optional as $dir => $errorMsg) {
|
||||
$is_writable = is_writable(BASE . $dir) && (MYAAC_OS != 'WINDOWS' || win_is_writable(BASE . $dir));
|
||||
version_check($locale['step_requirements_write_perms'] . ': ' . $dir, $is_writable, $is_writable ? '' : $errorMsg, true);
|
||||
}
|
||||
|
||||
$ini_register_globals = ini_get_bool('register_globals');
|
||||
version_check('register_long_arrays', !$ini_register_globals, $ini_register_globals ? $locale['on'] : $locale['off']);
|
||||
|
||||
$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(str_replace('$EXTENSION$', 'PDO', $locale['step_requirements_extension']) , extension_loaded('pdo'), extension_loaded('pdo') ? $locale['loaded'] : $locale['not_loaded']);
|
||||
version_check(str_replace('$EXTENSION$', 'XML', $locale['step_requirements_extension']), extension_loaded('xml'), extension_loaded('xml') ? $locale['loaded'] : $locale['not_loaded']);
|
||||
version_check(str_replace('$EXTENSION$', 'ZIP', $locale['step_requirements_extension']), extension_loaded('zip'), extension_loaded('zip') ? $locale['loaded'] : $locale['not_loaded']);
|
||||
|
||||
if($failed)
|
||||
{
|
||||
echo '<br/><b>' . $locale['step_requirements_failed'];
|
||||
echo next_form(true, false);
|
||||
foreach ($extensions_required as $ext) {
|
||||
$loaded = extension_loaded($ext);
|
||||
version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded']);
|
||||
}
|
||||
else
|
||||
|
||||
foreach ($extensions_optional as $ext => $errorMsg) {
|
||||
$loaded = extension_loaded($ext);
|
||||
version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded'] . '. ' . $errorMsg, true);
|
||||
}
|
||||
|
||||
echo '<div class="text-center m-3">';
|
||||
|
||||
if($failed) {
|
||||
echo '<div class="alert alert-warning"><span>' . $locale['step_requirements_failed'] . '</span></div>';
|
||||
echo next_form(true, false);
|
||||
}else {
|
||||
echo next_form(true, true);
|
||||
?>
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
?>
|
||||
|
@ -21,8 +21,6 @@ if(!$error) {
|
||||
// user can disable when he wants
|
||||
$content .= '$config[\'env\'] = \'prod\'; // dev or prod';
|
||||
$content .= PHP_EOL;
|
||||
$content .= '$config[\'mail_enabled\'] = true;';
|
||||
$content .= PHP_EOL;
|
||||
foreach($_SESSION as $key => $value)
|
||||
{
|
||||
if(strpos($key, 'var_') !== false)
|
||||
@ -67,10 +65,6 @@ if(!$error) {
|
||||
error($locale['step_config_mail_admin_error']);
|
||||
$error = true;
|
||||
}
|
||||
if(!Validator::email($_SESSION['var_mail_address'])) {
|
||||
error($locale['step_config_mail_address_error']);
|
||||
$error = true;
|
||||
}
|
||||
|
||||
$content .= '$config[\'session_prefix\'] = \'myaac_' . generateRandomString(8, true, false, true, false) . '_\';';
|
||||
$content .= PHP_EOL;
|
||||
@ -82,6 +76,7 @@ if(!$error) {
|
||||
}
|
||||
|
||||
if($saved) {
|
||||
success($locale['step_database_config_saved']);
|
||||
if(!$error) {
|
||||
$_SESSION['saved'] = true;
|
||||
}
|
||||
@ -100,8 +95,10 @@ if(!$error) {
|
||||
}
|
||||
?>
|
||||
|
||||
<form action="<?php echo BASE_URL; ?>install/" method="post">
|
||||
<input type="hidden" name="step" id="step" value="admin" />
|
||||
<?php echo next_buttons(true, $error ? false : true);
|
||||
?>
|
||||
</form>
|
||||
<div class="text-center m-3">
|
||||
<form action="<?php echo BASE_URL; ?>install/" method="post">
|
||||
<input type="hidden" name="step" id="step" value="admin" />
|
||||
<?php echo next_buttons(true, $error ? false : true);
|
||||
?>
|
||||
</form>
|
||||
</div>
|
@ -1,299 +1,13 @@
|
||||
* {
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400&display=swap');
|
||||
|
||||
body {
|
||||
text-align: center;
|
||||
font: 12px Verdana;
|
||||
color: #000000;
|
||||
background-color: #000000;
|
||||
}
|
||||
img {
|
||||
border: 0;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.break {
|
||||
font-size: 0;
|
||||
width: 0; height: 0;
|
||||
clear: both;
|
||||
}
|
||||
.alignleft {
|
||||
float: left;
|
||||
margin: 4px 10px 5px 0;
|
||||
}
|
||||
.alignright {
|
||||
float: right;
|
||||
margin: 4px 0 5px 10px;
|
||||
}
|
||||
.aligncenter {
|
||||
text-align: center;
|
||||
h1{
|
||||
font-weight: 100 !important;
|
||||
}
|
||||
|
||||
/** BEGIN wrapper **/
|
||||
#wrapper {
|
||||
background: #ffffff url(images/background.jpg) repeat-x 0 0;
|
||||
width: 980px;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#header span {
|
||||
font-size: 25px;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
padding-left: 40px;
|
||||
line-height: 80px;
|
||||
}
|
||||
|
||||
#version {
|
||||
float: right;
|
||||
color: #000;
|
||||
font-size: 17px;
|
||||
padding-top: 25px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
/** BEGIN body **/
|
||||
#body {
|
||||
background: url(images/wrapper.gif) repeat-y 0 0;
|
||||
}
|
||||
/** END body **/
|
||||
|
||||
/** BEGIN content **/
|
||||
#content {
|
||||
width: 642px;
|
||||
float: left;
|
||||
padding: 20px 18px 20px 20px;
|
||||
color: #434242;
|
||||
}
|
||||
|
||||
/** begin headers **/
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: Tahoma;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h2, h3, h4, h5, h6 {
|
||||
margin-top: 30px;
|
||||
}
|
||||
h1 { font-size: 2em; }
|
||||
h2 { font-size: 1.6em; }
|
||||
h3 { font-size: 1.3em; }
|
||||
h4, h5, h6 { font-size: 1em; }
|
||||
/** end headers **/
|
||||
|
||||
/** begin messages **/
|
||||
.error, .success, .note, .warning {
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
padding: 4px 10px 4px 24px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 5px 6px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
line-height: 1.6em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.error {
|
||||
background-color: #FDD9D9;
|
||||
background-image: url(images/error.gif);
|
||||
border-color: #FBA3A3;
|
||||
color: #D80303;
|
||||
}
|
||||
.success {
|
||||
background-color: #E4FCD9;
|
||||
background-image: url(images/success.gif);
|
||||
border-color: #BFFDA3;
|
||||
color: #35A502;
|
||||
}
|
||||
.note {
|
||||
background-color: #DDEAFA;
|
||||
background-image: url(images/note.gif);
|
||||
border-color: #A3D8FD;
|
||||
color: #026DA5;
|
||||
}
|
||||
.warning {
|
||||
background-color: #FBF0B3;
|
||||
background-image: url(images/warning.gif);
|
||||
border-color: #FBBB95;
|
||||
color: #FD6002;
|
||||
}
|
||||
/** end messages **/
|
||||
|
||||
/** begin form **/
|
||||
form {
|
||||
border: 1px solid #DDDDDD;
|
||||
padding: 16px;
|
||||
}
|
||||
form .input {
|
||||
padding-top: 12px;
|
||||
clear: both;
|
||||
}
|
||||
form .first {
|
||||
padding-top: 0;
|
||||
}
|
||||
form .input p {
|
||||
margin-bottom: 7px !important;
|
||||
}
|
||||
form input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
form label {
|
||||
margin-right: 10px;
|
||||
color: #8B8B8B;
|
||||
}
|
||||
form input.text, form textarea {
|
||||
border: 1px solid #BEBDBD;
|
||||
font-size: 1em;
|
||||
font-family: Verdana;
|
||||
background-color: #F3F3F3;
|
||||
color: #808080;
|
||||
padding: 2px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.positive, .negative {
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
padding: 1px 0 0 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 0;
|
||||
display: inline;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.positive {
|
||||
background-image: url(images/positive.gif);
|
||||
color: #35A502;
|
||||
}
|
||||
.negative {
|
||||
background-image: url(images/negative.gif);
|
||||
color: #D80303;
|
||||
}
|
||||
form textarea {
|
||||
line-height: 1.6em;
|
||||
}
|
||||
form button, form input.button {
|
||||
font-size: 0.9em;
|
||||
font-family: Verdana;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
background: #B6B4B4 url(images/button.gif) repeat-x 0 0;
|
||||
border: 1px solid #B6B4B4;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
/** end form **/
|
||||
|
||||
/** begin table **/
|
||||
table {
|
||||
|
||||
}
|
||||
table th {
|
||||
font-size: 0.9em;
|
||||
color: #ffffff;
|
||||
background-color: #679BC5;
|
||||
padding: 2px 4px;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
table td {
|
||||
line-height: 1.6em;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
table tr.odd td { background-color: #EEEEEE; }
|
||||
table tr.even td { background-color: #E5E5E5; }
|
||||
|
||||
/** end table **/
|
||||
|
||||
/** begin paragraphs, lists, etc. **/
|
||||
#content p {
|
||||
line-height: 1.6em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#content ul, #content ol {
|
||||
list-style-position: inside;
|
||||
}
|
||||
#content li {
|
||||
line-height: 1.6em;
|
||||
padding: 2px 0 2px 0;
|
||||
}
|
||||
a {
|
||||
color: #679BC5;
|
||||
}
|
||||
a:hover {
|
||||
color: #ff0000;
|
||||
text-decoration: none;
|
||||
}
|
||||
blockquote {
|
||||
padding: 10px;
|
||||
background-color: #eeeeee;
|
||||
line-height: 1.6em;
|
||||
border-width: 2px 0 1px;
|
||||
border-style: solid;
|
||||
border-color: #e0e0e0;
|
||||
}
|
||||
/** end paragraphs, lists, etc. **/
|
||||
|
||||
/** END content **/
|
||||
|
||||
/** BEGIN sidebar **/
|
||||
#sidebar {
|
||||
width: 300px;
|
||||
float: right;
|
||||
padding: 10px 0;
|
||||
}
|
||||
#sidebar h2 {
|
||||
background: green url(images/sidehead.gif) no-repeat 0 0;
|
||||
margin: 0 10px;
|
||||
font-size: 1em;
|
||||
color: #ffffff;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
#sidebar ul {
|
||||
list-style-type: none;
|
||||
background: #E0E0E0 url(images/sidebody.gif) no-repeat 0 bottom;
|
||||
padding: 10px;
|
||||
margin: 0 10px 10px;
|
||||
}
|
||||
#sidebar ul li {
|
||||
padding: 4px 0 4px 14px;
|
||||
background: none;
|
||||
line-height: 1.6em;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
}
|
||||
#sidebar ul li a {
|
||||
color: #000000;
|
||||
text-decoration: none;
|
||||
}
|
||||
#sidebar ul li a:hover {
|
||||
text-decoration: none;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
#sidebar ul li a:active {
|
||||
text-decoration: none;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
#sidebar ul li current {
|
||||
text-decoration: none;
|
||||
color: #ff0000;
|
||||
}
|
||||
.current {
|
||||
text-decoration: none;
|
||||
color: #ff0000;
|
||||
}
|
||||
h3 {
|
||||
font-weight: 300 !important;
|
||||
}
|
@ -1,48 +1,74 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>">
|
||||
<!DOCTYPE html>
|
||||
<html dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo $locale['encoding']; ?>" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>MyAAC - <?php echo $locale['installation']; ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="template/style.css" />
|
||||
<script type="text/javascript" src="<?php echo BASE_URL; ?>tools/js/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<!--div class="buffer"-->
|
||||
<div id="header">
|
||||
<h1>MyAAC <?php echo $locale['installation']; ?></h1>
|
||||
</div>
|
||||
|
||||
<div id="body">
|
||||
<div id="body" class="container">
|
||||
|
||||
<header id="header" class="pt-5 pb-4 pb-sm-5">
|
||||
<h1>MyAAC <?php echo $locale['installation']; ?></h1>
|
||||
</header>
|
||||
|
||||
<div id="sidebar">
|
||||
<h2><?php echo $locale['steps']; ?></h2>
|
||||
<ul>
|
||||
<?php
|
||||
$i = 0;
|
||||
foreach($steps as $key => $value)
|
||||
echo '<li' . ($step == $value ? ' class="current"' : '') . '>' . ++$i . '. ' . $locale['step_' . $value] . '</li>';
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
<div id="content">
|
||||
<div id="sidebar" class="col-md-3">
|
||||
<h3><?php echo $locale['steps']; ?></h3>
|
||||
<ul class="list-group mt-4">
|
||||
<?php
|
||||
if(isset($locale['step_' . $step . '_title']))
|
||||
echo '<h1>' . $locale['step_' . $step . '_title'] . '</h1>';
|
||||
else
|
||||
echo '<h1>' . $locale['step_' . $step] . '</h1>';
|
||||
echo $content;
|
||||
$i = 0;
|
||||
foreach($steps as $key => $value){
|
||||
|
||||
if ($step == $value) {
|
||||
$progress = ($i == 6) ? 100 : $i * 16;
|
||||
}
|
||||
|
||||
echo '<li' . ($step == $value ? ' class="list-group-item active"' : ' class="list-group-item"') . '>' . ++$i . '. ' . $locale['step_' . $value] . '</li>';
|
||||
}
|
||||
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="break"></div>
|
||||
</ul>
|
||||
</div>
|
||||
<!--/div-->
|
||||
|
||||
<div id="content" class="col-md-9">
|
||||
|
||||
<?php
|
||||
if(isset($locale['step_' . $step . '_title']))
|
||||
echo '<h3 class="mb-4 mt-4 mt-md-0">' . $locale['step_' . $step . '_title'] . '</h3>';
|
||||
else
|
||||
echo '<h3 class="mb-4 mt-4 mt-md-0">' . $locale['step_' . $step] . '</h3>';
|
||||
?>
|
||||
|
||||
<?php
|
||||
if(!isset($config['installed'])):
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="progress mb-2">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: <?php echo $progress; ?>%" role="progressbar" aria-valuenow="<?php echo $progress; ?>" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php echo $content; ?>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<footer id="footer" class="p-4">
|
||||
<p style="text-align: center;"><?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?></p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
define('MYAAC_INSTALL', true);
|
||||
|
||||
require_once '../../common.php';
|
||||
|
||||
require SYSTEM . 'functions.php';
|
||||
@ -48,7 +50,6 @@ else {
|
||||
try {
|
||||
$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']);
|
||||
success($locale['step_database_success_schema']);
|
||||
}
|
||||
@ -247,4 +248,4 @@ if($db->hasTable('z_forum')) {
|
||||
success($locale['step_database_adding_field'] . ' z_forum.closed...');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
define('MYAAC_INSTALL', true);
|
||||
|
||||
require_once '../../common.php';
|
||||
|
||||
require SYSTEM . 'functions.php';
|
||||
@ -43,38 +45,9 @@ if($success) {
|
||||
success($locale['step_database_imported_players']);
|
||||
}
|
||||
|
||||
require LIBS . 'items.php';
|
||||
if(Items::loadFromXML())
|
||||
success($locale['step_database_loaded_items']);
|
||||
else
|
||||
error(Items::getError());
|
||||
|
||||
require LIBS . 'weapons.php';
|
||||
if(Weapons::loadFromXML())
|
||||
success($locale['step_database_loaded_weapons']);
|
||||
else
|
||||
error(Weapons::getError());
|
||||
|
||||
require LIBS . 'creatures.php';
|
||||
if(Creatures::loadFromXML()) {
|
||||
success($locale['step_database_loaded_monsters']);
|
||||
|
||||
if(Creatures::getMonstersList()->hasErrors()) {
|
||||
$locale['step_database_error_monsters'] = str_replace('$LOG$', 'system/logs/error.log', $locale['step_database_error_monsters']);
|
||||
warning($locale['step_database_error_monsters']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(Creatures::getLastError());
|
||||
}
|
||||
|
||||
require LIBS . 'spells.php';
|
||||
if(Spells::loadFromXML()) {
|
||||
success($locale['step_database_loaded_spells']);
|
||||
}
|
||||
else {
|
||||
error(Spells::getLastError());
|
||||
}
|
||||
require LIBS . 'DataLoader.php';
|
||||
DataLoader::setLocale($locale);
|
||||
DataLoader::load();
|
||||
|
||||
// update config.highscores_ids_hidden
|
||||
require_once SYSTEM . 'migrations/20.php';
|
||||
@ -97,4 +70,4 @@ $locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_repl
|
||||
$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('$LINK$', generateLink('https://my-aac.org', 'https://my-aac.org', true), $locale['step_finish_desc']);
|
||||
|
||||
success($locale['step_finish_desc']);
|
||||
success($locale['step_finish_desc']);
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
|
||||
$config['clients'] = array(
|
||||
$config['clients'] = [
|
||||
710,
|
||||
740,
|
||||
750,
|
||||
@ -54,7 +54,9 @@ $config['clients'] = array(
|
||||
|
||||
1000,
|
||||
1010,
|
||||
1020,
|
||||
1021,
|
||||
1030,
|
||||
1031,
|
||||
1034,
|
||||
1041,
|
||||
@ -62,6 +64,7 @@ $config['clients'] = array(
|
||||
1053,
|
||||
1054,
|
||||
1058,
|
||||
1070,
|
||||
1075,
|
||||
1077,
|
||||
1079,
|
||||
@ -74,5 +77,16 @@ $config['clients'] = array(
|
||||
1097,
|
||||
1098,
|
||||
1100,
|
||||
);
|
||||
?>
|
||||
1102,
|
||||
1140,
|
||||
1150,
|
||||
1180,
|
||||
1200,
|
||||
1202,
|
||||
1215,
|
||||
1220,
|
||||
1230,
|
||||
1240,
|
||||
1251,
|
||||
1260,
|
||||
];
|
||||
|
@ -8,6 +8,7 @@
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Twig\Loader\ArrayLoader as Twig_ArrayLoader;
|
||||
|
||||
defined('MYAAC') or die('Direct access not allowed!');
|
||||
@ -23,10 +24,12 @@ function message($message, $type, $return)
|
||||
return true;
|
||||
}
|
||||
|
||||
if($return)
|
||||
return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
|
||||
if($return) {
|
||||
// for install and admin pages use bootstrap classes
|
||||
return '<div class="' . ((defined('MYAAC_INSTALL') || defined('MYAAC_ADMIN')) ? 'alert alert-' : '') . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
|
||||
}
|
||||
|
||||
echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
|
||||
echo '<div class="' . ((defined('MYAAC_INSTALL') || defined('MYAAC_ADMIN')) ? 'alert alert-' : '') . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
|
||||
return true;
|
||||
}
|
||||
function success($message, $return = false) {
|
||||
@ -39,28 +42,9 @@ function note($message, $return = false) {
|
||||
return message($message, 'note', $return);
|
||||
}
|
||||
function error($message, $return = false) {
|
||||
return message($message, 'error', $return);
|
||||
return message($message, ((defined('MYAAC_INSTALL') || defined('MYAAC_ADMIN')) ? 'danger' : 'error'), $return);
|
||||
}
|
||||
function message1($head, $message, $type, $icon , $return)
|
||||
{//return '<div class="' . $type . '">' . $message . '</div>';
|
||||
if($return)
|
||||
return '<div class="alert alert-'.$type.' alert-dismissible"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button><h4><i class="icon fa fa-'.$icon.'"></i> '.$head.':</h4>'.$message.'</div>';
|
||||
|
||||
echo '<div class="alert alert-'.$type.' alert-dismissible"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button><h4><i class="icon fa fa-'.$icon.'"></i> '.$head.':</h4>'.$message.'</div>';
|
||||
return true;
|
||||
}
|
||||
function success1($message, $return = false) {
|
||||
return message('Info', $message, 'success','success', $return);
|
||||
}
|
||||
function warning1($message, $return = false) {
|
||||
return message('Warning',$message, 'warning','ban', $return);
|
||||
}
|
||||
function note1($message, $return = false) {
|
||||
return message('Info',$message, 'info','info', $return);
|
||||
}
|
||||
function error1($message, $return = false) {
|
||||
return message("Alert", $message, 'danger','check', $return);
|
||||
}
|
||||
function longToIp($ip)
|
||||
{
|
||||
$exp = explode(".", long2ip($ip));
|
||||
@ -112,6 +96,16 @@ function getPlayerLink($name, $generate = true)
|
||||
return generateLink($url, $name);
|
||||
}
|
||||
|
||||
function getMonsterLink($name, $generate = true)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$url = BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'creatures/' . urlencode($name);
|
||||
|
||||
if(!$generate) return $url;
|
||||
return generateLink($url, $name);
|
||||
}
|
||||
|
||||
function getHouseLink($name, $generate = true)
|
||||
{
|
||||
global $db, $config;
|
||||
@ -168,7 +162,24 @@ function getItemImage($id, $count = 1)
|
||||
$file_name .= '-' . $count;
|
||||
|
||||
global $config;
|
||||
return '<img src="' . $config['item_images_url'] . $file_name . '.gif"' . $tooltip . ' width="32" height="32" border="0" alt="' .$id . '" />';
|
||||
return '<img src="' . $config['item_images_url'] . $file_name . config('item_images_extension') . '"' . $tooltip . ' width="32" height="32" border="0" alt="' .$id . '" />';
|
||||
}
|
||||
|
||||
function getItemRarity($chance) {
|
||||
if ($chance >= 21) {
|
||||
return "common";
|
||||
} elseif (between($chance, 8, 21)) {
|
||||
return "uncommon";
|
||||
} elseif (between($chance, 1.1, 8)) {
|
||||
return "semi rare";
|
||||
} elseif (between($chance, 0.4, 1.1)) {
|
||||
return "rare";
|
||||
} elseif (between($chance, 0.8, 0.4)) {
|
||||
return "very rare";
|
||||
} elseif ($chance <= 0.8) {
|
||||
return "extremely rare";
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getFlagImage($country)
|
||||
@ -472,33 +483,16 @@ function template_place_holder($type)
|
||||
*/
|
||||
function template_header($is_admin = false)
|
||||
{
|
||||
global $title_full, $config;
|
||||
global $title_full, $config, $twig;
|
||||
$charset = isset($config['charset']) ? $config['charset'] : 'utf-8';
|
||||
|
||||
$ret = '
|
||||
<meta charset="' . $charset . '">
|
||||
<meta http-equiv="content-language" content="' . $config['language'] . '" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=' . $charset . '" />';
|
||||
if(!$is_admin)
|
||||
$ret .= '
|
||||
<base href="' . BASE_URL . '" />
|
||||
<title>' . $title_full . '</title>';
|
||||
|
||||
$ret .= '
|
||||
<meta name="description" content="' . $config['meta_description'] . '" />
|
||||
<meta name="keywords" content="' . $config['meta_keywords'] . ', myaac, wodzaac" />
|
||||
<meta name="generator" content="MyAAC" />
|
||||
<link rel="stylesheet" type="text/css" href="' . BASE_URL . 'tools/css/messages.css" />
|
||||
<script type="text/javascript" src="' . BASE_URL . 'tools/js/jquery.min.js"></script>
|
||||
<noscript>
|
||||
<div class="warning" style="text-align: center; font-size: 14px;">Your browser does not support JavaScript or its disabled!<br/>
|
||||
Please turn it on, or be aware that some features on this website will not work correctly.</div>
|
||||
</noscript>
|
||||
';
|
||||
|
||||
if($config['recaptcha_enabled'])
|
||||
$ret .= "<script src='https://www.google.com/recaptcha/api.js'></script>";
|
||||
return $ret;
|
||||
return $twig->render('templates.header.html.twig',
|
||||
[
|
||||
'charset' => $charset,
|
||||
'title' => $title_full,
|
||||
'is_admin' => $is_admin
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -565,10 +559,8 @@ function template_form()
|
||||
foreach($templates as $key => $value)
|
||||
$options .= '<option ' . ($template_name == $value ? 'SELECTED' : '') . '>' . $value . '</option>';
|
||||
|
||||
return '<form method="get" action="' . BASE_URL . '">
|
||||
<hidden name="subtopic" value="' . PAGE . '"/>
|
||||
<select name="template" onchange="this.form.submit()">' . $options . '</select>
|
||||
</form>';
|
||||
global $twig;
|
||||
return $twig->render('forms.change_template.html.twig', ['options' => $options]);
|
||||
}
|
||||
|
||||
function getStyle($i)
|
||||
@ -827,13 +819,16 @@ function getWorldName($id)
|
||||
*/
|
||||
function _mail($to, $subject, $body, $altBody = '', $add_html_tags = true)
|
||||
{
|
||||
/** @var PHPMailer $mailer */
|
||||
global $mailer, $config;
|
||||
|
||||
if (!config('mail_enabled')) {
|
||||
log_append('mailer-error.log', '_mail() function has been used, but config.mail_enabled is disabled.');
|
||||
}
|
||||
|
||||
if(!$mailer)
|
||||
{
|
||||
require SYSTEM . 'libs/phpmailer/PHPMailerAutoload.php';
|
||||
$mailer = new PHPMailer();
|
||||
$mailer->setLanguage('en', LIBS . 'phpmailer/language/');
|
||||
//$mailer->setLanguage('en', LIBS . 'phpmailer/language/');
|
||||
}
|
||||
else {
|
||||
$mailer->clearAllRecipients();
|
||||
@ -931,6 +926,12 @@ function load_config_lua($filename)
|
||||
if(count($lines) > 0) {
|
||||
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);
|
||||
if(strpos($line, 'dofile') !== false)
|
||||
{
|
||||
@ -957,16 +958,17 @@ function load_config_lua($filename)
|
||||
$result[$key] = (string) substr(substr($value, 1), 0, -1);
|
||||
elseif(in_array($value, array('true', 'false')))
|
||||
$result[$key] = ($value === 'true') ? true : false;
|
||||
elseif(@$value[0] === '{' && @$value[strlen($value) - 1] === '}') {
|
||||
elseif(@$value[0] === '{') {
|
||||
// arrays are not supported yet
|
||||
// just ignore the error
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
|
||||
$value = str_replace($tmp_key, $tmp_value, $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>]');
|
||||
}
|
||||
@ -991,6 +993,10 @@ function str_replace_first($search, $replace, $subject) {
|
||||
}
|
||||
|
||||
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']))
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP']))
|
||||
@ -1304,6 +1310,33 @@ function getBanType($typeId)
|
||||
return "Unknown Type";
|
||||
}
|
||||
|
||||
function getChangelogType($v)
|
||||
{
|
||||
switch($v) {
|
||||
case 1:
|
||||
return 'added';
|
||||
case 2:
|
||||
return 'removed';
|
||||
case 3:
|
||||
return 'changed';
|
||||
case 4:
|
||||
return 'fixed';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function getChangelogWhere($v)
|
||||
{
|
||||
switch($v) {
|
||||
case 1:
|
||||
return 'server';
|
||||
case 2:
|
||||
return 'website';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
function getPlayerNameByAccount($id)
|
||||
{
|
||||
global $vowels, $ots, $db;
|
||||
@ -1341,6 +1374,7 @@ function getPlayerNameByAccount($id)
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function echo_success($message)
|
||||
{
|
||||
echo '<div class="col-12 success mb-2">' . $message . '</div>';
|
||||
@ -1389,6 +1423,50 @@ function Outfits_loadfromXML()
|
||||
return array('id' => $looktype, 'type' => $type, 'name' => $lookname, 'premium' => $premium, 'unlocked' => $unlocked, 'enabled' => $enabled);
|
||||
}
|
||||
|
||||
function left($str, $length) {
|
||||
return substr($str, 0, $length);
|
||||
}
|
||||
|
||||
function right($str, $length) {
|
||||
return substr($str, -$length);
|
||||
}
|
||||
|
||||
function getCreatureImgPath($creature){
|
||||
$creature_path = config('creatures_images_url');
|
||||
$creature_gfx_name = trim(strtolower($creature)) . config('creatures_images_extension');
|
||||
if (!file_exists($creature_path . $creature_gfx_name)) {
|
||||
$creature_gfx_name = str_replace(" ", "", $creature_gfx_name);
|
||||
if (file_exists($creature_path . $creature_gfx_name)) {
|
||||
return $creature_path . $creature_gfx_name;
|
||||
} else {
|
||||
return $creature_path . 'nophoto.png';
|
||||
}
|
||||
} else {
|
||||
return $creature_path . $creature_gfx_name;
|
||||
}
|
||||
}
|
||||
|
||||
function between($x, $lim1, $lim2) {
|
||||
if ($lim1 < $lim2) {
|
||||
$lower = $lim1; $upper = $lim2;
|
||||
}
|
||||
else {
|
||||
$lower = $lim2; $upper = $lim1;
|
||||
}
|
||||
return (($x >= $lower) && ($x <= $upper));
|
||||
}
|
||||
|
||||
function truncate($string, $length)
|
||||
{
|
||||
if (strlen($string) > $length) {
|
||||
$string = substr($string, 0, $length) . '...';
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
// validator functions
|
||||
require_once LIBS . 'validator.php';
|
||||
require_once SYSTEM . 'compat.php';
|
||||
|
||||
// custom functions
|
||||
require SYSTEM . 'functions_custom.php';
|
||||
|
11
system/functions_custom.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom functions
|
||||
*
|
||||
* @package MyAAC
|
||||
* @author Slawkens <slawkens@gmail.com>, Lee
|
||||
* @copyright 2020 MyAAC
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
|
||||
// Insert your custom functions here.
|
@ -119,6 +119,8 @@ if(!isset($config['highscores_ids_hidden']) || count($config['highscores_ids_hid
|
||||
$config['highscores_ids_hidden'] = array(0);
|
||||
}
|
||||
|
||||
$config['account_create_character_create'] = config('account_create_character_create') && (!config('mail_enabled') || !config('account_mail_verify'));
|
||||
|
||||
// POT
|
||||
require_once SYSTEM . 'libs/pot/OTS.php';
|
||||
$ots = POT::getInstance();
|
||||
@ -140,10 +142,8 @@ else {
|
||||
if(!@file_exists($file))
|
||||
$file = $config['data_path'] . 'vocations.xml';
|
||||
|
||||
$vocations->load($file);
|
||||
|
||||
if(!$vocations)
|
||||
throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> 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.');
|
||||
|
||||
$config['vocations'] = array();
|
||||
foreach($vocations->getElementsByTagName('vocation') as $vocation) {
|
||||
@ -157,54 +157,5 @@ else {
|
||||
}
|
||||
unset($tmp, $id, $vocation);
|
||||
|
||||
// load towns
|
||||
/* TODO: doesnt work
|
||||
ini_set('memory_limit', '-1');
|
||||
$tmp = '';
|
||||
|
||||
if($cache->enabled() && $cache->fetch('towns', $tmp)) {
|
||||
$config['towns'] = unserialize($tmp);
|
||||
}
|
||||
else {
|
||||
$towns = new OTS_OTBMFile();
|
||||
$towns->loadFile('D:/Projekty/opentibia/wodzislawski/data/world/wodzislawski.otbm');
|
||||
|
||||
$config['towns'] = $towns->getTownsList();
|
||||
if($cache->enabled()) {
|
||||
$cache->set('towns', serialize($config['towns']), 120);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
////////////////////////////////////////
|
||||
// load towns from database (TFS 1.3) //
|
||||
////////////////////////////////////////
|
||||
|
||||
$tmp = '';
|
||||
$towns = [];
|
||||
if($cache->enabled() && $cache->fetch('towns', $tmp)) {
|
||||
$towns = unserialize($tmp);
|
||||
}
|
||||
else {
|
||||
if($db->hasTable('towns')) {
|
||||
$query = $db->query('SELECT `id`, `name` FROM `towns`;')->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach($query as $town) {
|
||||
$towns[$town['id']] = $town['name'];
|
||||
}
|
||||
|
||||
unset($query);
|
||||
}
|
||||
else {
|
||||
$towns = config('towns');
|
||||
}
|
||||
|
||||
if($cache->enabled()) {
|
||||
$cache->set('towns', serialize($towns), 600);
|
||||
}
|
||||
}
|
||||
|
||||
config(['towns', $towns]);
|
||||
//////////////////////////////////////////////
|
||||
// END - load towns from database (TFS 1.3) //
|
||||
//////////////////////////////////////////////
|
||||
require LIBS . 'Towns.php';
|
||||
Towns::load();
|
@ -35,6 +35,13 @@ class CreateCharacter
|
||||
}
|
||||
}
|
||||
|
||||
$player = new OTS_Player();
|
||||
$player->find($name);
|
||||
if($player->isLoaded()) {
|
||||
$errors['name'] = 'Character with this name already exist.';
|
||||
return false;
|
||||
}
|
||||
|
||||
if(empty($sex) && $sex != "0")
|
||||
$errors['sex'] = 'Please select the sex for your character!';
|
||||
|
||||
@ -96,7 +103,7 @@ class CreateCharacter
|
||||
|
||||
if(empty($errors))
|
||||
{
|
||||
$number_of_players_on_account = $account->getPlayersList()->count();
|
||||
$number_of_players_on_account = $account->getPlayersList(false)->count();
|
||||
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>!';
|
||||
}
|
||||
@ -214,4 +221,4 @@ class CreateCharacter
|
||||
$account->logAction('Created character <b>' . $name . '</b>.');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
113
system/libs/DataLoader.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* Project: MyAAC
|
||||
* Automatic Account Creator for Open Tibia Servers
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* @package MyAAC
|
||||
* @author Slawkens <slawkens@gmail.com>
|
||||
* @copyright 2020 MyAAC
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class DataLoader
|
||||
*/
|
||||
class DataLoader
|
||||
{
|
||||
private static $locale;
|
||||
private static $startTime;
|
||||
|
||||
/**
|
||||
* Load data from server
|
||||
*/
|
||||
public static function load()
|
||||
{
|
||||
self::$startTime = microtime(true);
|
||||
|
||||
require LIBS . 'items.php';
|
||||
if(Items::loadFromXML()) {
|
||||
success(self::$locale['step_database_loaded_items'] . self::getLoadedTime());
|
||||
}
|
||||
else {
|
||||
error(Items::getError());
|
||||
}
|
||||
|
||||
self::$startTime = microtime(true);
|
||||
|
||||
require LIBS . 'creatures.php';
|
||||
if(Creatures::loadFromXML()) {
|
||||
success(self::$locale['step_database_loaded_monsters'] . self::getLoadedTime());
|
||||
|
||||
if(Creatures::getMonstersList()->hasErrors()) {
|
||||
self::$locale['step_database_error_monsters'] = str_replace('$LOG$', 'system/logs/error.log', self::$locale['step_database_error_monsters']);
|
||||
warning(self::$locale['step_database_error_monsters']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(Creatures::getLastError());
|
||||
}
|
||||
|
||||
self::$startTime = microtime(true);
|
||||
|
||||
require LIBS . 'npc.php';
|
||||
if(NPCs::loadFromXML()) {
|
||||
success(self::$locale['step_database_loaded_npcs'] . self::getLoadedTime());
|
||||
}
|
||||
else {
|
||||
error(self::$locale['step_database_error_npcs']);
|
||||
}
|
||||
|
||||
self::$startTime = microtime(true);
|
||||
|
||||
require LIBS . 'spells.php';
|
||||
if(Spells::loadFromXML()) {
|
||||
success(self::$locale['step_database_loaded_spells'] . self::getLoadedTime());
|
||||
}
|
||||
else {
|
||||
error(Spells::getLastError());
|
||||
}
|
||||
|
||||
self::$startTime = microtime(true);
|
||||
|
||||
if (Towns::save()) {
|
||||
success(self::$locale['step_database_loaded_towns'] . self::getLoadedTime());
|
||||
}
|
||||
else {
|
||||
warning(self::$locale['step_database_error_towns']);
|
||||
}
|
||||
|
||||
self::$startTime = microtime(true);
|
||||
|
||||
require LIBS . 'weapons.php';
|
||||
if(Weapons::loadFromXML()) {
|
||||
success(self::$locale['step_database_loaded_weapons'] . self::getLoadedTime());
|
||||
}
|
||||
else {
|
||||
error(Weapons::getError());
|
||||
}
|
||||
}
|
||||
|
||||
public static function setLocale($locale) {
|
||||
self::$locale = $locale;
|
||||
}
|
||||
|
||||
private static function getLoadedTime()
|
||||
{
|
||||
$endTime = round(microtime(true) - self::$startTime, 3);
|
||||
return ' (' . str_replace('$TIME$', $endTime, self::$locale['loaded_in_ms']) . ')';
|
||||
}
|
||||
}
|
138
system/libs/Towns.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/**
|
||||
* Project: MyAAC
|
||||
* Automatic Account Creator for Open Tibia Servers
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* @package MyAAC
|
||||
* @author Slawkens <slawkens@gmail.com>
|
||||
* @copyright 2020 MyAAC
|
||||
* @link https://my-aac.org
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Towns
|
||||
*/
|
||||
class Towns
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $filename = CACHE . 'towns.php';
|
||||
|
||||
/**
|
||||
* Determine towns
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function determine()
|
||||
{
|
||||
global $db;
|
||||
|
||||
if($db->hasTable('towns')) {
|
||||
return self::getFromDatabase();
|
||||
}
|
||||
|
||||
return self::getFromOTBM();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load cached towns file
|
||||
*/
|
||||
public static function load()
|
||||
{
|
||||
$towns = config('towns');
|
||||
if (file_exists(self::$filename)) {
|
||||
$towns = require self::$filename;
|
||||
}
|
||||
|
||||
config(['towns', $towns]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save into cache file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function save()
|
||||
{
|
||||
$towns = self::determine();
|
||||
if (count($towns) > 0) {
|
||||
file_put_contents(self::$filename, '<?php return ' . var_export($towns, true) . ';', LOCK_EX);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load from OTBM map file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getFromOTBM()
|
||||
{
|
||||
$mapName = configLua('mapName');
|
||||
if (!isset($mapName)) {
|
||||
$mapName = configLua('map');
|
||||
$mapFile = config('server_path') . $mapName;
|
||||
}
|
||||
|
||||
if (strpos($mapName, '.otbm') === false) {
|
||||
$mapName .= '.otbm';
|
||||
}
|
||||
|
||||
if (!isset($mapFile)) {
|
||||
$mapFile = config('data_path') . 'world/' . $mapName;
|
||||
}
|
||||
|
||||
if (strpos($mapFile, '.gz') !== false) {
|
||||
$mapFile = str_replace('.gz', '', $mapFile);
|
||||
}
|
||||
|
||||
$towns = [];
|
||||
if (file_exists($mapFile)) {
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
require LIBS . 'TownsReader.php';
|
||||
$townsReader = new TownsReader($mapFile);
|
||||
$townsReader->load();
|
||||
|
||||
$towns = $townsReader->get();
|
||||
}
|
||||
|
||||
return $towns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load from database
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getFromDatabase()
|
||||
{
|
||||
global $db;
|
||||
|
||||
$query = $db->query('SELECT `id`, `name` FROM `towns`;')->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$towns = [];
|
||||
foreach($query as $town) {
|
||||
$towns[$town['id']] = $town['name'];
|
||||
}
|
||||
|
||||
return $towns;
|
||||
}
|
||||
}
|
82
system/libs/TownsReader.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/*
|
||||
This file is part of OTSCMS (http://www.otscms.com/) project.
|
||||
|
||||
Copyright (C) 2005 - 2007 Wrzasq (wrzasq@gmail.com)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
This code bases on oryginal OTServ code for .otbm files - file iomapotbm.cpp rev.2141
|
||||
*/
|
||||
class TownsReader
|
||||
{
|
||||
// node bytes
|
||||
const ESCAPE_CHAR = 0xFD;
|
||||
const NODE_START = 0xFE;
|
||||
|
||||
// map node types
|
||||
const OTBM_TOWN = 13;
|
||||
|
||||
// file handler
|
||||
protected $file;
|
||||
|
||||
// towns
|
||||
private $towns = [];
|
||||
|
||||
// loads map .otbm file
|
||||
public function __construct($file)
|
||||
{
|
||||
// opens file for reading
|
||||
$this->file = fopen($file, 'rb');
|
||||
}
|
||||
|
||||
public function load()
|
||||
{
|
||||
// checks if file is opened correctly
|
||||
if ($this->file) {
|
||||
// skips version
|
||||
fseek($this->file, 4);
|
||||
|
||||
// reads nodes chain
|
||||
while (!feof($this->file)) {
|
||||
// reads byte
|
||||
switch (ord(fgetc($this->file))) {
|
||||
// maybe a town node
|
||||
case self::NODE_START:
|
||||
// reads node type
|
||||
if (ord(fgetc($this->file)) == self::OTBM_TOWN) {
|
||||
$id = unpack('L', fread($this->file, 4));
|
||||
$length = unpack('S', fread($this->file, 2));
|
||||
|
||||
// reads town name
|
||||
$this->towns[$id[1]] = fread($this->file, $length[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
// escape next character - it might be NODE_START character which is in fact not
|
||||
case self::ESCAPE_CHAR:
|
||||
fgetc($this->file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function get() {
|
||||
return $this->towns;
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Autoloads Twig classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since 1.21 and will be removed in 2.0. Use Composer instead. 2.0.
|
||||
*/
|
||||
class Twig_Autoloader
|
||||
{
|
||||
/**
|
||||
* Registers Twig_Autoloader as an SPL autoloader.
|
||||
*
|
||||
* @param bool $prepend whether to prepend the autoloader or not
|
||||
*/
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
if (PHP_VERSION_ID < 50300) {
|
||||
spl_autoload_register(array(__CLASS__, 'autoload'));
|
||||
} else {
|
||||
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles autoloading of classes.
|
||||
*
|
||||
* @param string $class a class name
|
||||
*/
|
||||
public static function autoload($class)
|
||||
{
|
||||
if (0 !== strpos($class, 'Twig')) {// || !isset($class[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = __DIR__.'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php';
|
||||
|
||||
$dev_mode = (config('env') === 'dev');
|
||||
if($dev_mode && !is_file($file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
require $file;
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
<?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\Cache;
|
||||
|
||||
/**
|
||||
* Interface implemented by cache classes.
|
||||
*
|
||||
* It is highly recommended to always store templates on the filesystem to
|
||||
* benefit from the PHP opcode cache. This interface is mostly useful if you
|
||||
* need to implement a custom strategy for storing templates on the filesystem.
|
||||
*
|
||||
* @author Andrew Tch <andrew@noop.lv>
|
||||
*/
|
||||
interface CacheInterface
|
||||
{
|
||||
/**
|
||||
* Generates a cache key for the given template class name.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param string $className The template class name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateKey($name, $className);
|
||||
|
||||
/**
|
||||
* Writes the compiled template to cache.
|
||||
*
|
||||
* @param string $key The cache key
|
||||
* @param string $content The template representation as a PHP class
|
||||
*/
|
||||
public function write($key, $content);
|
||||
|
||||
/**
|
||||
* Loads a template from the cache.
|
||||
*
|
||||
* @param string $key The cache key
|
||||
*/
|
||||
public function load($key);
|
||||
|
||||
/**
|
||||
* Returns the modification timestamp of a key.
|
||||
*
|
||||
* @param string $key The cache key
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimestamp($key);
|
||||
}
|
||||
|
||||
class_alias('Twig\Cache\CacheInterface', 'Twig_CacheInterface');
|
@ -1,93 +0,0 @@
|
||||
<?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\Cache;
|
||||
|
||||
/**
|
||||
* Implements a cache on the filesystem.
|
||||
*
|
||||
* @author Andrew Tch <andrew@noop.lv>
|
||||
*/
|
||||
class FilesystemCache implements CacheInterface
|
||||
{
|
||||
const FORCE_BYTECODE_INVALIDATION = 1;
|
||||
|
||||
private $directory;
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @param string $directory The root cache directory
|
||||
* @param int $options A set of options
|
||||
*/
|
||||
public function __construct($directory, $options = 0)
|
||||
{
|
||||
$this->directory = rtrim($directory, '\/').'/';
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function generateKey($name, $className)
|
||||
{
|
||||
$hash = hash('sha256', $className);
|
||||
|
||||
return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
|
||||
}
|
||||
|
||||
public function load($key)
|
||||
{
|
||||
if (file_exists($key)) {
|
||||
@include_once $key;
|
||||
}
|
||||
}
|
||||
|
||||
public function write($key, $content)
|
||||
{
|
||||
$dir = \dirname($key);
|
||||
if (!is_dir($dir)) {
|
||||
if (false === @mkdir($dir, 0777, true)) {
|
||||
clearstatcache(true, $dir);
|
||||
if (!is_dir($dir)) {
|
||||
throw new \RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
|
||||
}
|
||||
}
|
||||
} elseif (!is_writable($dir)) {
|
||||
throw new \RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
|
||||
}
|
||||
|
||||
$tmpFile = tempnam($dir, basename($key));
|
||||
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) {
|
||||
@chmod($key, 0666 & ~umask());
|
||||
|
||||
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
|
||||
// Compile cached file into bytecode cache
|
||||
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
@opcache_invalidate($key, true);
|
||||
} elseif (\function_exists('apc_compile_file')) {
|
||||
apc_compile_file($key);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $key));
|
||||
}
|
||||
|
||||
public function getTimestamp($key)
|
||||
{
|
||||
if (!file_exists($key)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) @filemtime($key);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Cache\FilesystemCache', 'Twig_Cache_Filesystem');
|
@ -1,42 +0,0 @@
|
||||
<?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\Cache;
|
||||
|
||||
/**
|
||||
* Implements a no-cache strategy.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class NullCache implements CacheInterface
|
||||
{
|
||||
public function generateKey($name, $className)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function write($key, $content)
|
||||
{
|
||||
}
|
||||
|
||||
public function load($key)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTimestamp($key)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Cache\NullCache', 'Twig_Cache_Null');
|
@ -1,288 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Node\ModuleNode;
|
||||
|
||||
/**
|
||||
* Compiles a node to PHP code.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Compiler implements \Twig_CompilerInterface
|
||||
{
|
||||
protected $lastLine;
|
||||
protected $source;
|
||||
protected $indentation;
|
||||
protected $env;
|
||||
protected $debugInfo = [];
|
||||
protected $sourceOffset;
|
||||
protected $sourceLine;
|
||||
protected $filename;
|
||||
private $varNameSalt = 0;
|
||||
|
||||
public function __construct(Environment $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.
|
||||
*
|
||||
* @return Environment
|
||||
*/
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current PHP code after compilation.
|
||||
*
|
||||
* @return string The PHP code
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param int $indentation The current indentation
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function compile(\Twig_NodeInterface $node, $indentation = 0)
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
$this->debugInfo = [];
|
||||
$this->sourceOffset = 0;
|
||||
// source code starts at 1 (as we then increment it when we encounter new lines)
|
||||
$this->sourceLine = 1;
|
||||
$this->indentation = $indentation;
|
||||
$this->varNameSalt = 0;
|
||||
|
||||
if ($node instanceof ModuleNode) {
|
||||
// to be removed in 2.0
|
||||
$this->filename = $node->getTemplateName();
|
||||
}
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function subcompile(\Twig_NodeInterface $node, $raw = true)
|
||||
{
|
||||
if (false === $raw) {
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4);
|
||||
}
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a raw string to the compiled code.
|
||||
*
|
||||
* @param string $string The string
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function raw($string)
|
||||
{
|
||||
$this->source .= $string;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the compiled code by adding indentation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$strings = \func_get_args();
|
||||
foreach ($strings as $string) {
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4).$string;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param string $value The string
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function string($value)
|
||||
{
|
||||
$this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PHP representation of a given value.
|
||||
*
|
||||
* @param mixed $value The value to convert
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function repr($value)
|
||||
{
|
||||
if (\is_int($value) || \is_float($value)) {
|
||||
if (false !== $locale = setlocale(LC_NUMERIC, '0')) {
|
||||
setlocale(LC_NUMERIC, 'C');
|
||||
}
|
||||
|
||||
$this->raw(var_export($value, true));
|
||||
|
||||
if (false !== $locale) {
|
||||
setlocale(LC_NUMERIC, $locale);
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$this->raw('null');
|
||||
} elseif (\is_bool($value)) {
|
||||
$this->raw($value ? 'true' : 'false');
|
||||
} elseif (\is_array($value)) {
|
||||
$this->raw('[');
|
||||
$first = true;
|
||||
foreach ($value as $key => $v) {
|
||||
if (!$first) {
|
||||
$this->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
$this->repr($key);
|
||||
$this->raw(' => ');
|
||||
$this->repr($v);
|
||||
}
|
||||
$this->raw(']');
|
||||
} else {
|
||||
$this->string($value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds debugging information.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDebugInfo(\Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node->getTemplateLine() != $this->lastLine) {
|
||||
$this->write(sprintf("// line %d\n", $node->getTemplateLine()));
|
||||
|
||||
// when mbstring.func_overload is set to 2
|
||||
// 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->debugInfo[$this->sourceLine] = $node->getTemplateLine();
|
||||
|
||||
$this->lastLine = $node->getTemplateLine();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDebugInfo()
|
||||
{
|
||||
ksort($this->debugInfo);
|
||||
|
||||
return $this->debugInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indents the generated code.
|
||||
*
|
||||
* @param int $step The number of indentation to add
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function indent($step = 1)
|
||||
{
|
||||
$this->indentation += $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outdents the generated code.
|
||||
*
|
||||
* @param int $step The number of indentation to remove
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \LogicException When trying to outdent too much so the indentation would become negative
|
||||
*/
|
||||
public function outdent($step = 1)
|
||||
{
|
||||
// can't outdent by more steps than the current indentation level
|
||||
if ($this->indentation < $step) {
|
||||
throw new \LogicException('Unable to call outdent() as the indentation would become negative.');
|
||||
}
|
||||
|
||||
$this->indentation -= $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Compiler', 'Twig_Compiler');
|
@ -1,325 +0,0 @@
|
||||
<?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\Error;
|
||||
|
||||
use Twig\Source;
|
||||
use Twig\Template;
|
||||
|
||||
/**
|
||||
* Twig base exception.
|
||||
*
|
||||
* This exception class and its children must only be used when
|
||||
* an error occurs during the loading of a template, when a syntax error
|
||||
* is detected in a template, or when rendering a template. Other
|
||||
* errors must use regular PHP exception classes (like when the template
|
||||
* cache directory is not writable for instance).
|
||||
*
|
||||
* To help debugging template issues, this class tracks the original template
|
||||
* name and line where the error occurred.
|
||||
*
|
||||
* Whenever possible, you must set these information (original template name
|
||||
* and line number) yourself by passing them to the constructor. If some or all
|
||||
* these information are not available from where you throw the exception, then
|
||||
* this class will guess them automatically (when the line number is set to -1
|
||||
* and/or the name is set to null). As this is a costly operation, this
|
||||
* can be disabled by passing false for both the name and the line number
|
||||
* when creating a new instance of this class.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Error extends \Exception
|
||||
{
|
||||
protected $lineno;
|
||||
// to be renamed to name in 2.0
|
||||
protected $filename;
|
||||
protected $rawMessage;
|
||||
|
||||
private $sourcePath;
|
||||
private $sourceCode;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Set the line number to -1 to enable its automatic guessing.
|
||||
* Set the name to null to enable its automatic guessing.
|
||||
*
|
||||
* @param string $message The error message
|
||||
* @param int $lineno The template line where the error occurred
|
||||
* @param Source|string|null $source The source context where the error occurred
|
||||
* @param \Exception $previous The previous exception
|
||||
*/
|
||||
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null)
|
||||
{
|
||||
if (null === $source) {
|
||||
$name = null;
|
||||
} elseif (!$source instanceof Source) {
|
||||
// for compat with the Twig C ext., passing the template name as string is accepted
|
||||
$name = $source;
|
||||
} else {
|
||||
$name = $source->getName();
|
||||
$this->sourceCode = $source->getCode();
|
||||
$this->sourcePath = $source->getPath();
|
||||
}
|
||||
parent::__construct('', 0, $previous);
|
||||
|
||||
$this->lineno = $lineno;
|
||||
$this->filename = $name;
|
||||
$this->rawMessage = $message;
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw message.
|
||||
*
|
||||
* @return string The raw message
|
||||
*/
|
||||
public function getRawMessage()
|
||||
{
|
||||
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.
|
||||
*
|
||||
* @return int The template line
|
||||
*/
|
||||
public function getTemplateLine()
|
||||
{
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the template line where the error occurred.
|
||||
*
|
||||
* @param int $lineno The template line
|
||||
*/
|
||||
public function setTemplateLine($lineno)
|
||||
{
|
||||
$this->lineno = $lineno;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source context of the Twig template where the error occurred.
|
||||
*
|
||||
* @return Source|null
|
||||
*/
|
||||
public function getSourceContext()
|
||||
{
|
||||
return $this->filename ? new Source($this->sourceCode, $this->filename, $this->sourcePath) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source context of the Twig template where the error occurred.
|
||||
*/
|
||||
public function setSourceContext(Source $source = null)
|
||||
{
|
||||
if (null === $source) {
|
||||
$this->sourceCode = $this->filename = $this->sourcePath = null;
|
||||
} else {
|
||||
$this->sourceCode = $source->getCode();
|
||||
$this->filename = $source->getName();
|
||||
$this->sourcePath = $source->getPath();
|
||||
}
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function guess()
|
||||
{
|
||||
$this->guessTemplateInfo();
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function appendMessage($rawMessage)
|
||||
{
|
||||
$this->rawMessage .= $rawMessage;
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function updateRepr()
|
||||
{
|
||||
$this->message = $this->rawMessage;
|
||||
|
||||
if ($this->sourcePath && $this->lineno > 0) {
|
||||
$this->file = $this->sourcePath;
|
||||
$this->line = $this->lineno;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$dot = false;
|
||||
if ('.' === substr($this->message, -1)) {
|
||||
$this->message = substr($this->message, 0, -1);
|
||||
$dot = true;
|
||||
}
|
||||
|
||||
$questionMark = false;
|
||||
if ('?' === substr($this->message, -1)) {
|
||||
$this->message = substr($this->message, 0, -1);
|
||||
$questionMark = true;
|
||||
}
|
||||
|
||||
if ($this->filename) {
|
||||
if (\is_string($this->filename) || (\is_object($this->filename) && method_exists($this->filename, '__toString'))) {
|
||||
$name = sprintf('"%s"', $this->filename);
|
||||
} else {
|
||||
$name = json_encode($this->filename);
|
||||
}
|
||||
$this->message .= sprintf(' in %s', $name);
|
||||
}
|
||||
|
||||
if ($this->lineno && $this->lineno >= 0) {
|
||||
$this->message .= sprintf(' at line %d', $this->lineno);
|
||||
}
|
||||
|
||||
if ($dot) {
|
||||
$this->message .= '.';
|
||||
}
|
||||
|
||||
if ($questionMark) {
|
||||
$this->message .= '?';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function guessTemplateInfo()
|
||||
{
|
||||
$template = null;
|
||||
$templateClass = null;
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig_Template' !== \get_class($trace['object'])) {
|
||||
$currentClass = \get_class($trace['object']);
|
||||
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
|
||||
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
|
||||
$template = $trace['object'];
|
||||
$templateClass = \get_class($trace['object']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update template name
|
||||
if (null !== $template && null === $this->filename) {
|
||||
$this->filename = $template->getTemplateName();
|
||||
}
|
||||
|
||||
// update template path if any
|
||||
if (null !== $template && null === $this->sourcePath) {
|
||||
$src = $template->getSourceContext();
|
||||
$this->sourceCode = $src->getCode();
|
||||
$this->sourcePath = $src->getPath();
|
||||
}
|
||||
|
||||
if (null === $template || $this->lineno > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$r = new \ReflectionObject($template);
|
||||
$file = $r->getFileName();
|
||||
|
||||
$exceptions = [$e = $this];
|
||||
while ($e instanceof self && $e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
while ($e = array_pop($exceptions)) {
|
||||
$traces = $e->getTrace();
|
||||
array_unshift($traces, ['file' => $e->getFile(), 'line' => $e->getLine()]);
|
||||
|
||||
while ($trace = array_shift($traces)) {
|
||||
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
|
||||
if ($codeLine <= $trace['line']) {
|
||||
// update template line
|
||||
$this->lineno = $templateLine;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Error\Error', 'Twig_Error');
|
@ -1,23 +0,0 @@
|
||||
<?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\Error;
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs during template loading.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LoaderError extends Error
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('Twig\Error\LoaderError', 'Twig_Error_Loader');
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Error;
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs at runtime.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RuntimeError extends Error
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('Twig\Error\RuntimeError', 'Twig_Error_Runtime');
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Error;
|
||||
|
||||
/**
|
||||
* \Exception thrown when a syntax error occurs during lexing or parsing of a template.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SyntaxError extends Error
|
||||
{
|
||||
/**
|
||||
* Tweaks the error message to include suggestions.
|
||||
*
|
||||
* @param string $name The original name of the item that does not exist
|
||||
* @param array $items An array of possible 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 = [];
|
||||
foreach ($items as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) {
|
||||
$alternatives[$item] = $lev;
|
||||
}
|
||||
}
|
||||
asort($alternatives);
|
||||
|
||||
return array_keys($alternatives);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Error\SyntaxError', 'Twig_Error_Syntax');
|
@ -1,834 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ArrowFunctionExpression;
|
||||
use Twig\Node\Expression\AssignNameExpression;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
use Twig\Node\Expression\BlockReferenceExpression;
|
||||
use Twig\Node\Expression\ConditionalExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MethodCallExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\ParentExpression;
|
||||
use Twig\Node\Expression\Unary\NegUnary;
|
||||
use Twig\Node\Expression\Unary\NotUnary;
|
||||
use Twig\Node\Expression\Unary\PosUnary;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Parses expressions.
|
||||
*
|
||||
* This parser implements a "Precedence climbing" algorithm.
|
||||
*
|
||||
* @see https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
|
||||
* @see https://en.wikipedia.org/wiki/Operator-precedence_parser
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ExpressionParser
|
||||
{
|
||||
const OPERATOR_LEFT = 1;
|
||||
const OPERATOR_RIGHT = 2;
|
||||
|
||||
protected $parser;
|
||||
protected $unaryOperators;
|
||||
protected $binaryOperators;
|
||||
|
||||
private $env;
|
||||
|
||||
public function __construct(Parser $parser, $env = null)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
|
||||
if ($env instanceof Environment) {
|
||||
$this->env = $env;
|
||||
$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)
|
||||
{
|
||||
if ($allowArrow && $arrow = $this->parseArrow()) {
|
||||
return $arrow;
|
||||
}
|
||||
|
||||
$expr = $this->getPrimary();
|
||||
$token = $this->parser->getCurrentToken();
|
||||
while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
|
||||
$op = $this->binaryOperators[$token->getValue()];
|
||||
$this->parser->getStream()->next();
|
||||
|
||||
if ('is not' === $token->getValue()) {
|
||||
$expr = $this->parseNotTestExpression($expr);
|
||||
} elseif ('is' === $token->getValue()) {
|
||||
$expr = $this->parseTestExpression($expr);
|
||||
} elseif (isset($op['callable'])) {
|
||||
$expr = \call_user_func($op['callable'], $this->parser, $expr);
|
||||
} else {
|
||||
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
|
||||
$class = $op['class'];
|
||||
$expr = new $class($expr, $expr1, $token->getLine());
|
||||
}
|
||||
|
||||
$token = $this->parser->getCurrentToken();
|
||||
}
|
||||
|
||||
if (0 === $precedence) {
|
||||
return $this->parseConditionalExpression($expr);
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrowFunctionExpression|null
|
||||
*/
|
||||
private function parseArrow()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
// short array syntax (one argument, no parentheses)?
|
||||
if ($stream->look(1)->test(Token::ARROW_TYPE)) {
|
||||
$line = $stream->getCurrent()->getLine();
|
||||
$token = $stream->expect(Token::NAME_TYPE);
|
||||
$names = [new AssignNameExpression($token->getValue(), $token->getLine())];
|
||||
$stream->expect(Token::ARROW_TYPE);
|
||||
|
||||
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
|
||||
}
|
||||
|
||||
// first, determine if we are parsing an arrow function by finding => (long form)
|
||||
$i = 0;
|
||||
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
return null;
|
||||
}
|
||||
++$i;
|
||||
while (true) {
|
||||
// variable name
|
||||
++$i;
|
||||
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
return null;
|
||||
}
|
||||
++$i;
|
||||
if (!$stream->look($i)->test(Token::ARROW_TYPE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// yes, let's parse it properly
|
||||
$token = $stream->expect(Token::PUNCTUATION_TYPE, '(');
|
||||
$line = $token->getLine();
|
||||
|
||||
$names = [];
|
||||
while (true) {
|
||||
$token = $stream->expect(Token::NAME_TYPE);
|
||||
$names[] = new AssignNameExpression($token->getValue(), $token->getLine());
|
||||
|
||||
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')');
|
||||
$stream->expect(Token::ARROW_TYPE);
|
||||
|
||||
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
|
||||
}
|
||||
|
||||
protected function getPrimary()
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
|
||||
if ($this->isUnary($token)) {
|
||||
$operator = $this->unaryOperators[$token->getValue()];
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parseExpression($operator['precedence']);
|
||||
$class = $operator['class'];
|
||||
|
||||
return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
|
||||
} elseif ($token->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parseExpression();
|
||||
$this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
|
||||
|
||||
return $this->parsePostfixExpression($expr);
|
||||
}
|
||||
|
||||
return $this->parsePrimaryExpression();
|
||||
}
|
||||
|
||||
protected function parseConditionalExpression($expr)
|
||||
{
|
||||
while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) {
|
||||
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
} else {
|
||||
$expr2 = $expr;
|
||||
$expr3 = $this->parseExpression();
|
||||
}
|
||||
|
||||
$expr = new ConditionalExpression($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
protected function isUnary(Token $token)
|
||||
{
|
||||
return $token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
|
||||
}
|
||||
|
||||
protected function isBinary(Token $token)
|
||||
{
|
||||
return $token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
|
||||
}
|
||||
|
||||
public function parsePrimaryExpression()
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
switch ($token->getType()) {
|
||||
case Token::NAME_TYPE:
|
||||
$this->parser->getStream()->next();
|
||||
switch ($token->getValue()) {
|
||||
case 'true':
|
||||
case 'TRUE':
|
||||
$node = new ConstantExpression(true, $token->getLine());
|
||||
break;
|
||||
|
||||
case 'false':
|
||||
case 'FALSE':
|
||||
$node = new ConstantExpression(false, $token->getLine());
|
||||
break;
|
||||
|
||||
case 'none':
|
||||
case 'NONE':
|
||||
case 'null':
|
||||
case 'NULL':
|
||||
$node = new ConstantExpression(null, $token->getLine());
|
||||
break;
|
||||
|
||||
default:
|
||||
if ('(' === $this->parser->getCurrentToken()->getValue()) {
|
||||
$node = $this->getFunctionNode($token->getValue(), $token->getLine());
|
||||
} else {
|
||||
$node = new NameExpression($token->getValue(), $token->getLine());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Token::NUMBER_TYPE:
|
||||
$this->parser->getStream()->next();
|
||||
$node = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case Token::STRING_TYPE:
|
||||
case Token::INTERPOLATION_START_TYPE:
|
||||
$node = $this->parseStringExpression();
|
||||
break;
|
||||
|
||||
case Token::OPERATOR_TYPE:
|
||||
if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
$node = new NameExpression($token->getValue(), $token->getLine());
|
||||
break;
|
||||
} elseif (isset($this->unaryOperators[$token->getValue()])) {
|
||||
$class = $this->unaryOperators[$token->getValue()]['class'];
|
||||
|
||||
$ref = new \ReflectionClass($class);
|
||||
$negClass = 'Twig\Node\Expression\Unary\NegUnary';
|
||||
$posClass = 'Twig\Node\Expression\Unary\PosUnary';
|
||||
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'))
|
||||
) {
|
||||
throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
|
||||
}
|
||||
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parsePrimaryExpression();
|
||||
|
||||
$node = new $class($expr, $token->getLine());
|
||||
break;
|
||||
}
|
||||
|
||||
// no break
|
||||
default:
|
||||
if ($token->test(Token::PUNCTUATION_TYPE, '[')) {
|
||||
$node = $this->parseArrayExpression();
|
||||
} elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) {
|
||||
$node = $this->parseHashExpression();
|
||||
} elseif ($token->test(Token::OPERATOR_TYPE, '=') && ('==' === $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());
|
||||
} else {
|
||||
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parsePostfixExpression($node);
|
||||
}
|
||||
|
||||
public function parseStringExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$nodes = [];
|
||||
// a string cannot be followed by another string in a single expression
|
||||
$nextCanBeString = true;
|
||||
while (true) {
|
||||
if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) {
|
||||
$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) {
|
||||
$nodes[] = $this->parseExpression();
|
||||
$stream->expect(Token::INTERPOLATION_END_TYPE);
|
||||
$nextCanBeString = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$expr = array_shift($nodes);
|
||||
foreach ($nodes as $node) {
|
||||
$expr = new ConcatBinary($expr, $node, $node->getTemplateLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
public function parseArrayExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
|
||||
|
||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$node->addElement($this->parseExpression());
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseHashExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
|
||||
|
||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
// a hash key can be:
|
||||
//
|
||||
// * a number -- 12
|
||||
// * a string -- 'a'
|
||||
// * a name, which is equivalent to a string -- a
|
||||
// * 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)) {
|
||||
$key = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
$key = $this->parseExpression();
|
||||
} else {
|
||||
$current = $stream->getCurrent();
|
||||
|
||||
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 (:)');
|
||||
$value = $this->parseExpression();
|
||||
|
||||
$node->addElement($value, $key);
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parsePostfixExpression($node)
|
||||
{
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if (Token::PUNCTUATION_TYPE == $token->getType()) {
|
||||
if ('.' == $token->getValue() || '[' == $token->getValue()) {
|
||||
$node = $this->parseSubscriptExpression($node);
|
||||
} elseif ('|' == $token->getValue()) {
|
||||
$node = $this->parseFilterExpression($node);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getFunctionNode($name, $line)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'parent':
|
||||
$this->parseArguments();
|
||||
if (!\count($this->parser->getBlockStack())) {
|
||||
throw new SyntaxError('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext());
|
||||
}
|
||||
|
||||
if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
|
||||
throw new SyntaxError('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext());
|
||||
}
|
||||
|
||||
return new ParentExpression($this->parser->peekBlockStack(), $line);
|
||||
case 'block':
|
||||
$args = $this->parseArguments();
|
||||
if (\count($args) < 1) {
|
||||
throw new SyntaxError('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext());
|
||||
}
|
||||
|
||||
return new BlockReferenceExpression($args->getNode(0), \count($args) > 1 ? $args->getNode(1) : null, $line);
|
||||
case 'attribute':
|
||||
$args = $this->parseArguments();
|
||||
if (\count($args) < 2) {
|
||||
throw new SyntaxError('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext());
|
||||
}
|
||||
|
||||
return new GetAttrExpression($args->getNode(0), $args->getNode(1), \count($args) > 2 ? $args->getNode(2) : null, Template::ANY_CALL, $line);
|
||||
default:
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
|
||||
$arguments = new ArrayExpression([], $line);
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
|
||||
$node = new MethodCallExpression($alias['node'], $alias['name'], $arguments, $line);
|
||||
$node->setAttribute('safe', true);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
$args = $this->parseArguments(true);
|
||||
$class = $this->getFunctionNodeClass($name, $line);
|
||||
|
||||
return new $class($name, $args, $line);
|
||||
}
|
||||
}
|
||||
|
||||
public function parseSubscriptExpression($node)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$token = $stream->next();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new ArrayExpression([], $lineno);
|
||||
$type = Template::ANY_CALL;
|
||||
if ('.' == $token->getValue()) {
|
||||
$token = $stream->next();
|
||||
if (
|
||||
Token::NAME_TYPE == $token->getType()
|
||||
||
|
||||
Token::NUMBER_TYPE == $token->getType()
|
||||
||
|
||||
(Token::OPERATOR_TYPE == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))
|
||||
) {
|
||||
$arg = new ConstantExpression($token->getValue(), $lineno);
|
||||
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
$type = Template::METHOD_CALL;
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
|
||||
}
|
||||
|
||||
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||
if (!$arg instanceof ConstantExpression) {
|
||||
throw new SyntaxError(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$name = $arg->getAttribute('value');
|
||||
|
||||
if ($this->parser->isReservedMacroName($name)) {
|
||||
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);
|
||||
|
||||
return $node;
|
||||
}
|
||||
} else {
|
||||
$type = Template::ARRAY_CALL;
|
||||
|
||||
// slice?
|
||||
$slice = false;
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$arg = new ConstantExpression(0, $token->getLine());
|
||||
} else {
|
||||
$arg = $this->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
}
|
||||
|
||||
if ($slice) {
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
$length = new ConstantExpression(null, $token->getLine());
|
||||
} else {
|
||||
$length = $this->parseExpression();
|
||||
}
|
||||
|
||||
$class = $this->getFilterNodeClass('slice', $token->getLine());
|
||||
$arguments = new Node([$arg, $length]);
|
||||
$filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine());
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']');
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']');
|
||||
}
|
||||
|
||||
return new GetAttrExpression($node, $arg, $arguments, $type, $lineno);
|
||||
}
|
||||
|
||||
public function parseFilterExpression($node)
|
||||
{
|
||||
$this->parser->getStream()->next();
|
||||
|
||||
return $this->parseFilterExpressionRaw($node);
|
||||
}
|
||||
|
||||
public function parseFilterExpressionRaw($node, $tag = null)
|
||||
{
|
||||
while (true) {
|
||||
$token = $this->parser->getStream()->expect(Token::NAME_TYPE);
|
||||
|
||||
$name = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = new Node();
|
||||
} else {
|
||||
$arguments = $this->parseArguments(true, false, true);
|
||||
}
|
||||
|
||||
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
|
||||
|
||||
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
|
||||
|
||||
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '|')) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses arguments.
|
||||
*
|
||||
* @param bool $namedArguments Whether to allow named arguments or not
|
||||
* @param bool $definition Whether we are parsing arguments for a function definition
|
||||
*
|
||||
* @return Node
|
||||
*
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
public function parseArguments($namedArguments = false, $definition = false, $allowArrow = false)
|
||||
{
|
||||
$args = [];
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
if (!empty($args)) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
}
|
||||
|
||||
if ($definition) {
|
||||
$token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');
|
||||
$value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine());
|
||||
} else {
|
||||
$value = $this->parseExpression(0, $allowArrow);
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && $token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) {
|
||||
if (!$value instanceof NameExpression) {
|
||||
throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
$name = $value->getAttribute('name');
|
||||
|
||||
if ($definition) {
|
||||
$value = $this->parsePrimaryExpression();
|
||||
|
||||
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());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression(0, $allowArrow);
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition) {
|
||||
if (null === $name) {
|
||||
$name = $value->getAttribute('name');
|
||||
$value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
$args[$name] = $value;
|
||||
} else {
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
|
||||
return new Node($args);
|
||||
}
|
||||
|
||||
public function parseAssignmentExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$targets = [];
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
} else {
|
||||
$stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to');
|
||||
}
|
||||
$value = $token->getValue();
|
||||
if (\in_array(strtolower($value), ['true', 'false', 'none', 'null'])) {
|
||||
throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
$targets[] = new AssignNameExpression($value, $token->getLine());
|
||||
|
||||
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Node($targets);
|
||||
}
|
||||
|
||||
public function parseMultitargetExpression()
|
||||
{
|
||||
$targets = [];
|
||||
while (true) {
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Node($targets);
|
||||
}
|
||||
|
||||
private function parseNotTestExpression(\Twig_NodeInterface $node)
|
||||
{
|
||||
return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
private function parseTestExpression(\Twig_NodeInterface $node)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
list($name, $test) = $this->getTest($node->getTemplateLine());
|
||||
|
||||
$class = $this->getTestNodeClass($test);
|
||||
$arguments = null;
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = $this->parseArguments(true);
|
||||
}
|
||||
|
||||
return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
private function getTest($line)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$name = $stream->expect(Token::NAME_TYPE)->getValue();
|
||||
|
||||
if ($test = $this->env->getTest($name)) {
|
||||
return [$name, $test];
|
||||
}
|
||||
|
||||
if ($stream->test(Token::NAME_TYPE)) {
|
||||
// try 2-words tests
|
||||
$name = $name.' '.$this->parser->getCurrentToken()->getValue();
|
||||
|
||||
if ($test = $this->env->getTest($name)) {
|
||||
$stream->next();
|
||||
|
||||
return [$name, $test];
|
||||
}
|
||||
}
|
||||
|
||||
$e = new SyntaxError(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext());
|
||||
$e->addSuggestions($name, array_keys($this->env->getTests()));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
private function getTestNodeClass($test)
|
||||
{
|
||||
if ($test instanceof TwigTest && $test->isDeprecated()) {
|
||||
$stream = $this->parser->getStream();
|
||||
$message = sprintf('Twig Test "%s" is deprecated', $test->getName());
|
||||
if (!\is_bool($test->getDeprecatedVersion())) {
|
||||
$message .= sprintf(' since version %s', $test->getDeprecatedVersion());
|
||||
}
|
||||
if ($test->getAlternative()) {
|
||||
$message .= sprintf('. Use "%s" instead', $test->getAlternative());
|
||||
}
|
||||
$src = $stream->getSourceContext();
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine());
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($test instanceof TwigTest) {
|
||||
return $test->getNodeClass();
|
||||
}
|
||||
|
||||
return $test instanceof \Twig_Test_Node ? $test->getClass() : 'Twig\Node\Expression\TestExpression';
|
||||
}
|
||||
|
||||
protected function getFunctionNodeClass($name, $line)
|
||||
{
|
||||
if (false === $function = $this->env->getFunction($name)) {
|
||||
$e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext());
|
||||
$e->addSuggestions($name, array_keys($this->env->getFunctions()));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($function instanceof TwigFunction && $function->isDeprecated()) {
|
||||
$message = sprintf('Twig Function "%s" is deprecated', $function->getName());
|
||||
if (!\is_bool($function->getDeprecatedVersion())) {
|
||||
$message .= sprintf(' since version %s', $function->getDeprecatedVersion());
|
||||
}
|
||||
if ($function->getAlternative()) {
|
||||
$message .= sprintf('. Use "%s" instead', $function->getAlternative());
|
||||
}
|
||||
$src = $this->parser->getStream()->getSourceContext();
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($function instanceof TwigFunction) {
|
||||
return $function->getNodeClass();
|
||||
}
|
||||
|
||||
return $function instanceof \Twig_Function_Node ? $function->getClass() : 'Twig\Node\Expression\FunctionExpression';
|
||||
}
|
||||
|
||||
protected function getFilterNodeClass($name, $line)
|
||||
{
|
||||
if (false === $filter = $this->env->getFilter($name)) {
|
||||
$e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext());
|
||||
$e->addSuggestions($name, array_keys($this->env->getFilters()));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($filter instanceof TwigFilter && $filter->isDeprecated()) {
|
||||
$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
|
||||
if (!\is_bool($filter->getDeprecatedVersion())) {
|
||||
$message .= sprintf(' since version %s', $filter->getDeprecatedVersion());
|
||||
}
|
||||
if ($filter->getAlternative()) {
|
||||
$message .= sprintf('. Use "%s" instead', $filter->getAlternative());
|
||||
}
|
||||
$src = $this->parser->getStream()->getSourceContext();
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($filter instanceof TwigFilter) {
|
||||
return $filter->getNodeClass();
|
||||
}
|
||||
|
||||
return $filter instanceof \Twig_Filter_Node ? $filter->getClass() : 'Twig\Node\Expression\FilterExpression';
|
||||
}
|
||||
|
||||
// checks that the node only contains "constant" elements
|
||||
protected function checkConstantExpression(\Twig_NodeInterface $node)
|
||||
{
|
||||
if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression
|
||||
|| $node instanceof NegUnary || $node instanceof PosUnary
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$this->checkConstantExpression($n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\ExpressionParser', 'Twig_ExpressionParser');
|
@ -1,72 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
|
||||
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()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getOperators()
|
||||
{
|
||||
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');
|
@ -1,76 +0,0 @@
|
||||
<?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\Extension {
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DebugExtension extends AbstractExtension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
// dump is safe if var_dump is overridden by xdebug
|
||||
$isDumpOutputHtmlSafe = \extension_loaded('xdebug')
|
||||
// false means that it was not set (and the default is on) or it explicitly enabled
|
||||
&& (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
|
||||
// false means that it was not set (and the default is on) or it explicitly enabled
|
||||
// xdebug.overload_var_dump produces HTML only when html_errors is also enabled
|
||||
&& (false === ini_get('html_errors') || ini_get('html_errors'))
|
||||
|| 'cli' === \PHP_SAPI
|
||||
;
|
||||
|
||||
return [
|
||||
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');
|
||||
}
|
||||
|
||||
namespace {
|
||||
use Twig\Environment;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
|
||||
function twig_var_dump(Environment $env, $context, array $vars = [])
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if (!$vars) {
|
||||
$vars = [];
|
||||
foreach ($context as $key => $value) {
|
||||
if (!$value instanceof Template && !$value instanceof TemplateWrapper) {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
var_dump($vars);
|
||||
} else {
|
||||
foreach ($vars as $var) {
|
||||
var_dump($var);
|
||||
}
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
<?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\Extension {
|
||||
use Twig\NodeVisitor\EscaperNodeVisitor;
|
||||
use Twig\TokenParser\AutoEscapeTokenParser;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class EscaperExtension extends AbstractExtension
|
||||
{
|
||||
protected $defaultStrategy;
|
||||
|
||||
/**
|
||||
* @param string|false|callable $defaultStrategy An escaping strategy
|
||||
*
|
||||
* @see setDefaultStrategy()
|
||||
*/
|
||||
public function __construct($defaultStrategy = 'html')
|
||||
{
|
||||
$this->setDefaultStrategy($defaultStrategy);
|
||||
}
|
||||
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return [new AutoEscapeTokenParser()];
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [new EscaperNodeVisitor()];
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default strategy to use when not defined by the user.
|
||||
*
|
||||
* The strategy can be a valid PHP callback that takes the template
|
||||
* name as an argument and returns the strategy to use.
|
||||
*
|
||||
* @param string|false|callable $defaultStrategy An escaping strategy
|
||||
*/
|
||||
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) {
|
||||
$defaultStrategy = ['\Twig\FileExtensionEscapingStrategy', 'guess'];
|
||||
}
|
||||
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default strategy to use when not defined by the user.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string|false The default strategy to use for the template
|
||||
*/
|
||||
public function getDefaultStrategy($name)
|
||||
{
|
||||
// disable string callables to avoid calling a function named html or js,
|
||||
// or any other upcoming escaping strategy
|
||||
if (!\is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
|
||||
return \call_user_func($this->defaultStrategy, $name);
|
||||
}
|
||||
|
||||
return $this->defaultStrategy;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'escaper';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* Marks a variable as being safe.
|
||||
*
|
||||
* @param string $string A PHP variable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function twig_raw_filter($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Interface implemented by extension classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return TokenParserInterface[]
|
||||
*/
|
||||
public function getTokenParsers();
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return NodeVisitorInterface[]
|
||||
*/
|
||||
public function getNodeVisitors();
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return TwigFilter[]
|
||||
*/
|
||||
public function getFilters();
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @return TwigTest[]
|
||||
*/
|
||||
public function getTests();
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return TwigFunction[]
|
||||
*/
|
||||
public function getFunctions();
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
*
|
||||
* @return array<array> First array of unary operators, second array of binary operators
|
||||
*/
|
||||
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');
|
||||
|
||||
// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name.
|
||||
class_exists('Twig\Environment');
|
@ -1,26 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
/**
|
||||
* Enables usage of the deprecated Twig\Extension\AbstractExtension::getGlobals() method.
|
||||
*
|
||||
* Explicitly implement this interface if you really need to implement the
|
||||
* deprecated getGlobals() method in your extensions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface GlobalsInterface
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface');
|
@ -1,26 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
/**
|
||||
* Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method.
|
||||
*
|
||||
* Explicitly implement this interface if you really need to implement the
|
||||
* deprecated initRuntime() method in your extensions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface InitRuntimeInterface
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface');
|
@ -1,39 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
use Twig\NodeVisitor\OptimizerNodeVisitor;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class OptimizerExtension extends AbstractExtension
|
||||
{
|
||||
protected $optimizers;
|
||||
|
||||
public function __construct($optimizers = -1)
|
||||
{
|
||||
$this->optimizers = $optimizers;
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [new OptimizerNodeVisitor($this->optimizers)];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'optimizer';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer');
|
@ -1,53 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
use Twig\Profiler\NodeVisitor\ProfilerNodeVisitor;
|
||||
use Twig\Profiler\Profile;
|
||||
|
||||
class ProfilerExtension extends AbstractExtension
|
||||
{
|
||||
private $actives = [];
|
||||
|
||||
public function __construct(Profile $profile)
|
||||
{
|
||||
$this->actives[] = $profile;
|
||||
}
|
||||
|
||||
public function enter(Profile $profile)
|
||||
{
|
||||
$this->actives[0]->addProfile($profile);
|
||||
array_unshift($this->actives, $profile);
|
||||
}
|
||||
|
||||
public function leave(Profile $profile)
|
||||
{
|
||||
$profile->leave();
|
||||
array_shift($this->actives);
|
||||
|
||||
if (1 === \count($this->actives)) {
|
||||
$this->actives[0]->leave();
|
||||
}
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [new ProfilerNodeVisitor(\get_class($this))];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'profiler';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\ProfilerExtension', 'Twig_Extension_Profiler');
|
@ -1,19 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
/**
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
interface RuntimeExtensionInterface
|
||||
{
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
use Twig\NodeVisitor\SandboxNodeVisitor;
|
||||
use Twig\Sandbox\SecurityPolicyInterface;
|
||||
use Twig\TokenParser\SandboxTokenParser;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SandboxExtension extends AbstractExtension
|
||||
{
|
||||
protected $sandboxedGlobally;
|
||||
protected $sandboxed;
|
||||
protected $policy;
|
||||
|
||||
public function __construct(SecurityPolicyInterface $policy, $sandboxed = false)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
$this->sandboxedGlobally = $sandboxed;
|
||||
}
|
||||
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return [new SandboxTokenParser()];
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [new SandboxNodeVisitor()];
|
||||
}
|
||||
|
||||
public function enableSandbox()
|
||||
{
|
||||
$this->sandboxed = true;
|
||||
}
|
||||
|
||||
public function disableSandbox()
|
||||
{
|
||||
$this->sandboxed = false;
|
||||
}
|
||||
|
||||
public function isSandboxed()
|
||||
{
|
||||
return $this->sandboxedGlobally || $this->sandboxed;
|
||||
}
|
||||
|
||||
public function isSandboxedGlobally()
|
||||
{
|
||||
return $this->sandboxedGlobally;
|
||||
}
|
||||
|
||||
public function setSecurityPolicy(SecurityPolicyInterface $policy)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
}
|
||||
|
||||
public function getSecurityPolicy()
|
||||
{
|
||||
return $this->policy;
|
||||
}
|
||||
|
||||
public function checkSecurity($tags, $filters, $functions)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkSecurity($tags, $filters, $functions);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMethodAllowed($obj, $method)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkMethodAllowed($obj, $method);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $method)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkPropertyAllowed($obj, $method);
|
||||
}
|
||||
}
|
||||
|
||||
public function ensureToStringAllowed($obj)
|
||||
{
|
||||
if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'sandbox';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox');
|
@ -1,117 +0,0 @@
|
||||
<?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\Extension;
|
||||
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
|
||||
/**
|
||||
* Internal class.
|
||||
*
|
||||
* This class is used by \Twig\Environment as a staging area and must not be used directly.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class StagingExtension extends AbstractExtension
|
||||
{
|
||||
protected $functions = [];
|
||||
protected $filters = [];
|
||||
protected $visitors = [];
|
||||
protected $tokenParsers = [];
|
||||
protected $globals = [];
|
||||
protected $tests = [];
|
||||
|
||||
public function addFunction($name, $function)
|
||||
{
|
||||
if (isset($this->functions[$name])) {
|
||||
@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);
|
||||
}
|
||||
|
||||
$this->functions[$name] = $function;
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function addFilter($name, $filter)
|
||||
{
|
||||
if (isset($this->filters[$name])) {
|
||||
@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);
|
||||
}
|
||||
|
||||
$this->filters[$name] = $filter;
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return $this->visitors;
|
||||
}
|
||||
|
||||
public function addTokenParser(TokenParserInterface $parser)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
$this->tokenParsers[$parser->getTag()] = $parser;
|
||||
}
|
||||
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return $this->tokenParsers;
|
||||
}
|
||||
|
||||
public function addGlobal($name, $value)
|
||||
{
|
||||
$this->globals[$name] = $value;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'staging';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging');
|
@ -1,54 +0,0 @@
|
||||
<?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\Extension {
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class StringLoaderExtension extends AbstractExtension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
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');
|
||||
}
|
||||
|
||||
namespace {
|
||||
use Twig\Environment;
|
||||
use Twig\TemplateWrapper;
|
||||
|
||||
/**
|
||||
* Loads a template from a string.
|
||||
*
|
||||
* {{ include(template_from_string("Hello {{ name }}")) }}
|
||||
*
|
||||
* @param string $template A template as a string or object implementing __toString()
|
||||
* @param string $name An optional name of the template to be used in error messages
|
||||
*
|
||||
* @return TemplateWrapper
|
||||
*/
|
||||
function twig_template_from_string(Environment $env, $template, $name = null)
|
||||
{
|
||||
return $env->createTemplate((string) $template, $name);
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Default autoescaping strategy based on file names.
|
||||
*
|
||||
* This strategy sets the HTML as the default autoescaping strategy,
|
||||
* but changes it based on the template name.
|
||||
*
|
||||
* Note that there is no runtime performance impact as the
|
||||
* default autoescaping strategy is set at compilation time.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FileExtensionEscapingStrategy
|
||||
{
|
||||
/**
|
||||
* Guesses the best autoescaping strategy based on the file name.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string|false The escaping strategy name to use or false to disable
|
||||
*/
|
||||
public static function guess($name)
|
||||
{
|
||||
if (\in_array(substr($name, -1), ['/', '\\'])) {
|
||||
return 'html'; // return html for directories
|
||||
}
|
||||
|
||||
if ('.twig' === substr($name, -5)) {
|
||||
$name = substr($name, 0, -5);
|
||||
}
|
||||
|
||||
$extension = pathinfo($name, PATHINFO_EXTENSION);
|
||||
|
||||
switch ($extension) {
|
||||
case 'js':
|
||||
return 'js';
|
||||
|
||||
case 'css':
|
||||
return 'css';
|
||||
|
||||
case 'txt':
|
||||
return false;
|
||||
|
||||
default:
|
||||
return 'html';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\FileExtensionEscapingStrategy', 'Twig_FileExtensionEscapingStrategy');
|
@ -1,534 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
|
||||
/**
|
||||
* Lexes a template string.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Lexer implements \Twig_LexerInterface
|
||||
{
|
||||
protected $tokens;
|
||||
protected $code;
|
||||
protected $cursor;
|
||||
protected $lineno;
|
||||
protected $end;
|
||||
protected $state;
|
||||
protected $states;
|
||||
protected $brackets;
|
||||
protected $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;
|
||||
|
||||
const STATE_DATA = 0;
|
||||
const STATE_BLOCK = 1;
|
||||
const STATE_VAR = 2;
|
||||
const STATE_STRING = 3;
|
||||
const STATE_INTERPOLATION = 4;
|
||||
|
||||
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';
|
||||
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
const PUNCTUATION = '()[]{}?:.,|';
|
||||
|
||||
public function __construct(Environment $env, array $options = [])
|
||||
{
|
||||
$this->env = $env;
|
||||
|
||||
$this->options = array_merge([
|
||||
'tag_comment' => ['{#', '#}'],
|
||||
'tag_block' => ['{%', '%}'],
|
||||
'tag_variable' => ['{{', '}}'],
|
||||
'whitespace_trim' => '-',
|
||||
'whitespace_line_trim' => '~',
|
||||
'whitespace_line_chars' => ' \t\0\x0B',
|
||||
'interpolation' => ['#{', '}'],
|
||||
], $options);
|
||||
|
||||
// when PHP 7.3 is the min version, we will be able to remove the '#' part in preg_quote as it's part of the default
|
||||
$this->regexes = [
|
||||
// }}
|
||||
'lex_var' => '{
|
||||
\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '#').'\s*'. // -}}\s*
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_variable'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~}}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_variable'][1], '#'). // }}
|
||||
')
|
||||
}Ax',
|
||||
|
||||
// %}
|
||||
'lex_block' => '{
|
||||
\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*\n?'. // -%}\s*\n?
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][1], '#').'\n?'. // %}\n?
|
||||
')
|
||||
}Ax',
|
||||
|
||||
// {% endverbatim %}
|
||||
'lex_raw_data' => '{'.
|
||||
preg_quote($this->options['tag_block'][0], '#'). // {%
|
||||
'('.
|
||||
$this->options['whitespace_trim']. // -
|
||||
'|'.
|
||||
$this->options['whitespace_line_trim']. // ~
|
||||
')?\s*'.
|
||||
'(?:end%s)'. // endraw or endverbatim
|
||||
'\s*'.
|
||||
'(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][1], '#'). // %}
|
||||
')
|
||||
}sx',
|
||||
|
||||
'operator' => $this->getOperatorRegex(),
|
||||
|
||||
// #}
|
||||
'lex_comment' => '{
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim']).preg_quote($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['tag_comment'][1], '#').'\n?'. // #}\n?
|
||||
')
|
||||
}sx',
|
||||
|
||||
// verbatim %}
|
||||
'lex_block_raw' => '{
|
||||
\s*
|
||||
(raw|verbatim)
|
||||
\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}\s*
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][1], '#'). // %}
|
||||
')
|
||||
}Asx',
|
||||
|
||||
'lex_block_line' => '{\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '#').'}As',
|
||||
|
||||
// {{ or {% or {#
|
||||
'lex_tokens_start' => '{
|
||||
('.
|
||||
preg_quote($this->options['tag_variable'][0], '#'). // {{
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][0], '#'). // {%
|
||||
'|'.
|
||||
preg_quote($this->options['tag_comment'][0], '#'). // {#
|
||||
')('.
|
||||
preg_quote($this->options['whitespace_trim'], '#'). // -
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'], '#'). // ~
|
||||
')?
|
||||
}sx',
|
||||
'interpolation_start' => '{'.preg_quote($this->options['interpolation'][0], '#').'\s*}A',
|
||||
'interpolation_end' => '{\s*'.preg_quote($this->options['interpolation'][1], '#').'}A',
|
||||
];
|
||||
}
|
||||
|
||||
public function tokenize($code, $name = null)
|
||||
{
|
||||
if (!$code instanceof 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->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->lineno = 1;
|
||||
$this->end = \strlen($this->code);
|
||||
$this->tokens = [];
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->states = [];
|
||||
$this->brackets = [];
|
||||
$this->position = -1;
|
||||
|
||||
// find all token starts in one go
|
||||
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
|
||||
$this->positions = $matches;
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
// dispatch to the lexing functions depending
|
||||
// on the current state
|
||||
switch ($this->state) {
|
||||
case self::STATE_DATA:
|
||||
$this->lexData();
|
||||
break;
|
||||
|
||||
case self::STATE_BLOCK:
|
||||
$this->lexBlock();
|
||||
break;
|
||||
|
||||
case self::STATE_VAR:
|
||||
$this->lexVar();
|
||||
break;
|
||||
|
||||
case self::STATE_STRING:
|
||||
$this->lexString();
|
||||
break;
|
||||
|
||||
case self::STATE_INTERPOLATION:
|
||||
$this->lexInterpolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::EOF_TYPE);
|
||||
|
||||
if (!empty($this->brackets)) {
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
|
||||
if ($mbEncoding) {
|
||||
mb_internal_encoding($mbEncoding);
|
||||
}
|
||||
|
||||
return new TokenStream($this->tokens, $this->source);
|
||||
}
|
||||
|
||||
protected function lexData()
|
||||
{
|
||||
// if no matches are left we return the rest of the template as simple text token
|
||||
if ($this->position == \count($this->positions[0]) - 1) {
|
||||
$this->pushToken(Token::TEXT_TYPE, substr($this->code, $this->cursor));
|
||||
$this->cursor = $this->end;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first token after the current cursor
|
||||
$position = $this->positions[0][++$this->position];
|
||||
while ($position[1] < $this->cursor) {
|
||||
if ($this->position == \count($this->positions[0]) - 1) {
|
||||
return;
|
||||
}
|
||||
$position = $this->positions[0][++$this->position];
|
||||
}
|
||||
|
||||
// push the template text first
|
||||
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
|
||||
|
||||
// trim?
|
||||
if (isset($this->positions[2][$this->position][0])) {
|
||||
if ($this->options['whitespace_trim'] === $this->positions[2][$this->position][0]) {
|
||||
// whitespace_trim detected ({%-, {{- or {#-)
|
||||
$text = rtrim($text);
|
||||
} elseif ($this->options['whitespace_line_trim'] === $this->positions[2][$this->position][0]) {
|
||||
// whitespace_line_trim detected ({%~, {{~ or {#~)
|
||||
// don't trim \r and \n
|
||||
$text = rtrim($text, " \t\0\x0B");
|
||||
}
|
||||
}
|
||||
$this->pushToken(Token::TEXT_TYPE, $text);
|
||||
$this->moveCursor($textContent.$position[0]);
|
||||
|
||||
switch ($this->positions[1][$this->position][0]) {
|
||||
case $this->options['tag_comment'][0]:
|
||||
$this->lexComment();
|
||||
break;
|
||||
|
||||
case $this->options['tag_block'][0]:
|
||||
// raw data?
|
||||
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lexRawData($match[1]);
|
||||
// {% line \d+ %}
|
||||
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lineno = (int) $match[1];
|
||||
} else {
|
||||
$this->pushToken(Token::BLOCK_START_TYPE);
|
||||
$this->pushState(self::STATE_BLOCK);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
}
|
||||
break;
|
||||
|
||||
case $this->options['tag_variable'][0]:
|
||||
$this->pushToken(Token::VAR_START_TYPE);
|
||||
$this->pushState(self::STATE_VAR);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexBlock()
|
||||
{
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::BLOCK_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexVar()
|
||||
{
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::VAR_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexExpression()
|
||||
{
|
||||
// whitespace
|
||||
if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
if ($this->cursor >= $this->end) {
|
||||
throw new SyntaxError(sprintf('Unclosed "%s".', self::STATE_BLOCK === $this->state ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
// arrow function
|
||||
if ('=' === $this->code[$this->cursor] && '>' === $this->code[$this->cursor + 1]) {
|
||||
$this->pushToken(Token::ARROW_TYPE, '=>');
|
||||
$this->moveCursor('=>');
|
||||
}
|
||||
// operators
|
||||
elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// names
|
||||
elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::NAME_TYPE, $match[0]);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// numbers
|
||||
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) {
|
||||
$number = (float) $match[0]; // floats
|
||||
if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
|
||||
$number = (int) $match[0]; // integers lower than the maximum
|
||||
}
|
||||
$this->pushToken(Token::NUMBER_TYPE, $number);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// punctuation
|
||||
elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
|
||||
// opening bracket
|
||||
if (false !== strpos('([{', $this->code[$this->cursor])) {
|
||||
$this->brackets[] = [$this->code[$this->cursor], $this->lineno];
|
||||
}
|
||||
// closing bracket
|
||||
elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
|
||||
if (empty($this->brackets)) {
|
||||
throw new SyntaxError(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
|
||||
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
|
||||
++$this->cursor;
|
||||
}
|
||||
// strings
|
||||
elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// opening double quoted string
|
||||
elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->brackets[] = ['"', $this->lineno];
|
||||
$this->pushState(self::STATE_STRING);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// unlexable
|
||||
else {
|
||||
throw new SyntaxError(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexRawData($tag)
|
||||
{
|
||||
if ('raw' === $tag) {
|
||||
@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);
|
||||
}
|
||||
|
||||
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);
|
||||
$this->moveCursor($text.$match[0][0]);
|
||||
|
||||
// trim?
|
||||
if (isset($match[1][0])) {
|
||||
if ($this->options['whitespace_trim'] === $match[1][0]) {
|
||||
// whitespace_trim detected ({%-, {{- or {#-)
|
||||
$text = rtrim($text);
|
||||
} else {
|
||||
// whitespace_line_trim detected ({%~, {{~ or {#~)
|
||||
// don't trim \r and \n
|
||||
$text = rtrim($text, " \t\0\x0B");
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::TEXT_TYPE, $text);
|
||||
}
|
||||
|
||||
protected function lexComment()
|
||||
{
|
||||
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
|
||||
}
|
||||
|
||||
protected function lexString()
|
||||
{
|
||||
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->brackets[] = [$this->options['interpolation'][0], $this->lineno];
|
||||
$this->pushToken(Token::INTERPOLATION_START_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->pushState(self::STATE_INTERPOLATION);
|
||||
} 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->moveCursor($match[0]);
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
if ('"' != $this->code[$this->cursor]) {
|
||||
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
|
||||
$this->popState();
|
||||
++$this->cursor;
|
||||
} else {
|
||||
// unlexable
|
||||
throw new SyntaxError(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexInterpolation()
|
||||
{
|
||||
$bracket = end($this->brackets);
|
||||
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, 0, $this->cursor)) {
|
||||
array_pop($this->brackets);
|
||||
$this->pushToken(Token::INTERPOLATION_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function pushToken($type, $value = '')
|
||||
{
|
||||
// do not push empty text tokens
|
||||
if (Token::TEXT_TYPE === $type && '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tokens[] = new Token($type, $value, $this->lineno);
|
||||
}
|
||||
|
||||
protected function moveCursor($text)
|
||||
{
|
||||
$this->cursor += \strlen($text);
|
||||
$this->lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
protected function getOperatorRegex()
|
||||
{
|
||||
$operators = array_merge(
|
||||
['='],
|
||||
array_keys($this->env->getUnaryOperators()),
|
||||
array_keys($this->env->getBinaryOperators())
|
||||
);
|
||||
|
||||
$operators = array_combine($operators, array_map('strlen', $operators));
|
||||
arsort($operators);
|
||||
|
||||
$regex = [];
|
||||
foreach ($operators as $operator => $length) {
|
||||
// an operator that ends with a character must be followed by
|
||||
// a whitespace or a parenthesis
|
||||
if (ctype_alpha($operator[$length - 1])) {
|
||||
$r = preg_quote($operator, '/').'(?=[\s()])';
|
||||
} else {
|
||||
$r = preg_quote($operator, '/');
|
||||
}
|
||||
|
||||
// an operator with a space can be any amount of whitespaces
|
||||
$r = preg_replace('/\s+/', '\s+', $r);
|
||||
|
||||
$regex[] = $r;
|
||||
}
|
||||
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
}
|
||||
|
||||
protected function pushState($state)
|
||||
{
|
||||
$this->states[] = $this->state;
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
protected function popState()
|
||||
{
|
||||
if (0 === \count($this->states)) {
|
||||
throw new \LogicException('Cannot pop state without a previous state.');
|
||||
}
|
||||
|
||||
$this->state = array_pop($this->states);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Lexer', 'Twig_Lexer');
|
@ -1,102 +0,0 @@
|
||||
<?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\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads a template from an array.
|
||||
*
|
||||
* When using this loader with a cache mechanism, you should know that a new cache
|
||||
* key is generated each time a template content "changes" (the cache key being the
|
||||
* source code of the template). If you don't want to see your cache grows out of
|
||||
* control, you need to take care of clearing the old cache file by yourself.
|
||||
*
|
||||
* This loader should only be used for unit testing.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
{
|
||||
protected $templates = [];
|
||||
|
||||
/**
|
||||
* @param array $templates An array of templates (keys are the names, and values are the source code)
|
||||
*/
|
||||
public function __construct(array $templates = [])
|
||||
{
|
||||
$this->templates = $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or overrides a template.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param string $template The template source
|
||||
*/
|
||||
public function setTemplate($name, $template)
|
||||
{
|
||||
$this->templates[(string) $name] = $template;
|
||||
}
|
||||
|
||||
public function getSource($name)
|
||||
{
|
||||
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $this->templates[$name];
|
||||
}
|
||||
|
||||
public function getSourceContext($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return new Source($this->templates[$name], $name);
|
||||
}
|
||||
|
||||
public function exists($name)
|
||||
{
|
||||
return isset($this->templates[(string) $name]);
|
||||
}
|
||||
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $name.':'.$this->templates[$name];
|
||||
}
|
||||
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\ArrayLoader', 'Twig_Loader_Array');
|
@ -1,164 +0,0 @@
|
||||
<?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\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads templates from other loaders.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
{
|
||||
private $hasSourceCache = [];
|
||||
protected $loaders = [];
|
||||
|
||||
/**
|
||||
* @param LoaderInterface[] $loaders
|
||||
*/
|
||||
public function __construct(array $loaders = [])
|
||||
{
|
||||
foreach ($loaders as $loader) {
|
||||
$this->addLoader($loader);
|
||||
}
|
||||
}
|
||||
|
||||
public function addLoader(LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[] = $loader;
|
||||
$this->hasSourceCache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoaderInterface[]
|
||||
*/
|
||||
public function getLoaders()
|
||||
{
|
||||
return $this->loaders;
|
||||
}
|
||||
|
||||
public function getSource($name)
|
||||
{
|
||||
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getSource($name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
|
||||
public function getSourceContext($name)
|
||||
{
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
return $loader->getSourceContext($name);
|
||||
}
|
||||
|
||||
return new Source($loader->getSource($name), $name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
|
||||
public function exists($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
if (isset($this->hasSourceCache[$name])) {
|
||||
return $this->hasSourceCache[$name];
|
||||
}
|
||||
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface) {
|
||||
if ($loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext($name);
|
||||
} else {
|
||||
$loader->getSource($name);
|
||||
}
|
||||
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
} catch (LoaderError $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $this->hasSourceCache[$name] = false;
|
||||
}
|
||||
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getCacheKey($name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = \get_class($loader).': '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->isFresh($name, $time);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = \get_class($loader).': '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\ChainLoader', 'Twig_Loader_Chain');
|
@ -1,33 +0,0 @@
|
||||
<?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\Loader;
|
||||
|
||||
/**
|
||||
* Adds an exists() method for loaders.
|
||||
*
|
||||
* @author Florin Patan <florinpatan@gmail.com>
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
*/
|
||||
interface ExistsLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Check if we have the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name The name of the template to check if we can load
|
||||
*
|
||||
* @return bool If the template source code is handled by this loader or not
|
||||
*/
|
||||
public function exists($name);
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\ExistsLoaderInterface', 'Twig_ExistsLoaderInterface');
|
@ -1,323 +0,0 @@
|
||||
<?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\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads template from the filesystem.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
{
|
||||
/** Identifier of the main namespace. */
|
||||
const MAIN_NAMESPACE = '__main__';
|
||||
|
||||
protected $paths = [];
|
||||
protected $cache = [];
|
||||
protected $errorCache = [];
|
||||
|
||||
private $rootPath;
|
||||
|
||||
/**
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
* @param string|null $rootPath The root path common to all relative paths (null for getcwd())
|
||||
*/
|
||||
public function __construct($paths = [], $rootPath = null)
|
||||
{
|
||||
$this->rootPath = (null === $rootPath ? getcwd() : $rootPath).\DIRECTORY_SEPARATOR;
|
||||
if (false !== $realPath = realpath($rootPath)) {
|
||||
$this->rootPath = $realPath.\DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
if ($paths) {
|
||||
$this->setPaths($paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths to the templates.
|
||||
*
|
||||
* @param string $namespace A path namespace
|
||||
*
|
||||
* @return array The array of paths where to look for templates
|
||||
*/
|
||||
public function getPaths($namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path namespaces.
|
||||
*
|
||||
* The main namespace is always defined.
|
||||
*
|
||||
* @return array The array of defined namespaces
|
||||
*/
|
||||
public function getNamespaces()
|
||||
{
|
||||
return array_keys($this->paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the paths where templates are stored.
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
* @param string $namespace A path namespace
|
||||
*/
|
||||
public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
if (!\is_array($paths)) {
|
||||
$paths = [$paths];
|
||||
}
|
||||
|
||||
$this->paths[$namespace] = [];
|
||||
foreach ($paths as $path) {
|
||||
$this->addPath($path, $namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path where templates are stored.
|
||||
*
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $namespace A path namespace
|
||||
*
|
||||
* @throws LoaderError
|
||||
*/
|
||||
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = $this->errorCache = [];
|
||||
|
||||
$checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
|
||||
if (!is_dir($checkPath)) {
|
||||
throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
|
||||
}
|
||||
|
||||
$this->paths[$namespace][] = rtrim($path, '/\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a path where templates are stored.
|
||||
*
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $namespace A path namespace
|
||||
*
|
||||
* @throws LoaderError
|
||||
*/
|
||||
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = $this->errorCache = [];
|
||||
|
||||
$checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
|
||||
if (!is_dir($checkPath)) {
|
||||
throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
|
||||
}
|
||||
|
||||
$path = rtrim($path, '/\\');
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
$this->paths[$namespace][] = $path;
|
||||
} else {
|
||||
array_unshift($this->paths[$namespace], $path);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSource($name)
|
||||
{
|
||||
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
if (null === ($path = $this->findTemplate($name)) || false === $path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
public function getSourceContext($name)
|
||||
{
|
||||
if (null === ($path = $this->findTemplate($name)) || false === $path) {
|
||||
return new Source('', $name, '');
|
||||
}
|
||||
|
||||
return new Source(file_get_contents($path), $name, $path);
|
||||
}
|
||||
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
if (null === ($path = $this->findTemplate($name)) || false === $path) {
|
||||
return '';
|
||||
}
|
||||
$len = \strlen($this->rootPath);
|
||||
if (0 === strncmp($this->rootPath, $path, $len)) {
|
||||
return substr($path, $len);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function exists($name)
|
||||
{
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
return null !== ($path = $this->findTemplate($name, false)) && false !== $path;
|
||||
} catch (LoaderError $e) {
|
||||
@trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
// false support to be removed in 3.0
|
||||
if (null === ($path = $this->findTemplate($name)) || false === $path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filemtime($path) < $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the template can be found.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string|false|null The template name or false/null
|
||||
*/
|
||||
protected function findTemplate($name)
|
||||
{
|
||||
$throw = \func_num_args() > 1 ? func_get_arg(1) : true;
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
}
|
||||
|
||||
if (isset($this->errorCache[$name])) {
|
||||
if (!$throw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validateName($name);
|
||||
|
||||
list($namespace, $shortname) = $this->parseName($name);
|
||||
} catch (LoaderError $e) {
|
||||
if (!$throw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
$this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
|
||||
|
||||
if (!$throw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
foreach ($this->paths[$namespace] as $path) {
|
||||
if (!$this->isAbsolutePath($path)) {
|
||||
$path = $this->rootPath.$path;
|
||||
}
|
||||
|
||||
if (is_file($path.'/'.$shortname)) {
|
||||
if (false !== $realpath = realpath($path.'/'.$shortname)) {
|
||||
return $this->cache[$name] = $realpath;
|
||||
}
|
||||
|
||||
return $this->cache[$name] = $path.'/'.$shortname;
|
||||
}
|
||||
}
|
||||
|
||||
$this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
|
||||
|
||||
if (!$throw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
protected function parseName($name, $default = self::MAIN_NAMESPACE)
|
||||
{
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new LoaderError(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
$shortname = substr($name, $pos + 1);
|
||||
|
||||
return [$namespace, $shortname];
|
||||
}
|
||||
|
||||
return [$default, $name];
|
||||
}
|
||||
|
||||
protected function normalizeName($name)
|
||||
{
|
||||
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
|
||||
}
|
||||
|
||||
protected function validateName($name)
|
||||
{
|
||||
if (false !== strpos($name, "\0")) {
|
||||
throw new LoaderError('A template name cannot contain NUL bytes.');
|
||||
}
|
||||
|
||||
$name = ltrim($name, '/');
|
||||
$parts = explode('/', $name);
|
||||
$level = 0;
|
||||
foreach ($parts as $part) {
|
||||
if ('..' === $part) {
|
||||
--$level;
|
||||
} elseif ('.' !== $part) {
|
||||
++$level;
|
||||
}
|
||||
|
||||
if ($level < 0) {
|
||||
throw new LoaderError(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isAbsolutePath($file)
|
||||
{
|
||||
return strspn($file, '/\\', 0, 1)
|
||||
|| (\strlen($file) > 3 && ctype_alpha($file[0])
|
||||
&& ':' === substr($file, 1, 1)
|
||||
&& strspn($file, '/\\', 2, 1)
|
||||
)
|
||||
|| null !== parse_url($file, PHP_URL_SCHEME)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\FilesystemLoader', 'Twig_Loader_Filesystem');
|
@ -1,61 +0,0 @@
|
||||
<?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\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
|
||||
/**
|
||||
* Interface all loaders must implement.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name The name of the template to load
|
||||
*
|
||||
* @return string The template source code
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 2.0), implement Twig\Loader\SourceContextLoaderInterface
|
||||
*/
|
||||
public function getSource($name);
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @param string $name The name of the template to load
|
||||
*
|
||||
* @return string The cache key
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function getCacheKey($name);
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param int $time Timestamp of the last modification time of the
|
||||
* cached template
|
||||
*
|
||||
* @return bool true if the template is fresh, false otherwise
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function isFresh($name, $time);
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\LoaderInterface', 'Twig_LoaderInterface');
|
@ -1,38 +0,0 @@
|
||||
<?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\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Adds a getSourceContext() method for loaders.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 3.0)
|
||||
*/
|
||||
interface SourceContextLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Returns the source context for a given template logical name.
|
||||
*
|
||||
* @param string $name The template logical name
|
||||
*
|
||||
* @return Source
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function getSourceContext($name);
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\SourceContextLoaderInterface', 'Twig_SourceContextLoaderInterface');
|
@ -1,41 +0,0 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Marks a content as safe.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Markup implements \Countable
|
||||
{
|
||||
protected $content;
|
||||
protected $charset;
|
||||
|
||||
public function __construct($content, $charset)
|
||||
{
|
||||
$this->content = (string) $content;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return \function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : \strlen($this->content);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Markup', 'Twig_Markup');
|
@ -1,40 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents an autoescape node.
|
||||
*
|
||||
* The value is the escaping strategy (can be html, js, ...)
|
||||
*
|
||||
* The true value is equivalent to html.
|
||||
*
|
||||
* If autoescaping is disabled, then the value is false.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class AutoEscapeNode extends Node
|
||||
{
|
||||
public function __construct($value, \Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
|
||||
{
|
||||
parent::__construct(['body' => $body], ['value' => $value], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\AutoEscapeNode', 'Twig_Node_AutoEscape');
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a block node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class BlockNode extends Node
|
||||
{
|
||||
public function __construct($name, \Twig_NodeInterface $body, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(['body' => $body], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function block_%s(\$context, array \$blocks = [])\n", $this->getAttribute('name')), "{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\BlockNode', 'Twig_Node_Block');
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a block call node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class BlockReferenceNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct([], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\BlockReferenceNode', 'Twig_Node_BlockReference');
|
@ -1,23 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
/**
|
||||
* Represents a body node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class BodyNode extends Node
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\BodyNode', 'Twig_Node_Body');
|
@ -1,85 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CheckSecurityNode extends Node
|
||||
{
|
||||
protected $usedFilters;
|
||||
protected $usedTags;
|
||||
protected $usedFunctions;
|
||||
|
||||
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
|
||||
{
|
||||
$this->usedFilters = $usedFilters;
|
||||
$this->usedTags = $usedTags;
|
||||
$this->usedFunctions = $usedFunctions;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$tags = $filters = $functions = [];
|
||||
foreach (['tags', 'filters', 'functions'] as $type) {
|
||||
foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
|
||||
if ($node instanceof Node) {
|
||||
${$type}[$name] = $node->getTemplateLine();
|
||||
} else {
|
||||
${$type}[$node] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
|
||||
->write('$tags = ')->repr(array_filter($tags))->raw(";\n")
|
||||
->write('$filters = ')->repr(array_filter($filters))->raw(";\n")
|
||||
->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->write("\$this->sandbox->checkSecurity(\n")
|
||||
->indent()
|
||||
->write(!$tags ? "[],\n" : "['".implode("', '", array_keys($tags))."'],\n")
|
||||
->write(!$filters ? "[],\n" : "['".implode("', '", array_keys($filters))."'],\n")
|
||||
->write(!$functions ? "[]\n" : "['".implode("', '", array_keys($functions))."']\n")
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
->outdent()
|
||||
->write("} catch (SecurityError \$e) {\n")
|
||||
->indent()
|
||||
->write("\$e->setSourceContext(\$this->getSourceContext());\n\n")
|
||||
->write("if (\$e instanceof SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
|
||||
->outdent()
|
||||
->write("} elseif (\$e instanceof SecurityNotAllowedFilterError && isset(\$filters[\$e->getFilterName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$filters[\$e->getFilterName()]);\n")
|
||||
->outdent()
|
||||
->write("} elseif (\$e instanceof SecurityNotAllowedFunctionError && isset(\$functions[\$e->getFunctionName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$functions[\$e->getFunctionName()]);\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->write("throw \$e;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\CheckSecurityNode', 'Twig_Node_CheckSecurity');
|
@ -1,42 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
|
||||
/**
|
||||
* Checks if casting an expression to __toString() is allowed by the sandbox.
|
||||
*
|
||||
* For instance, when there is a simple Print statement, like {{ article }},
|
||||
* and if the sandbox is enabled, we need to check that the __toString()
|
||||
* method is allowed if 'article' is an object. The same goes for {{ article|upper }}
|
||||
* or {{ random(article) }}
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CheckToStringNode extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $expr)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $expr->getTemplateLine(), $expr->getNodeTag());
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('$this->sandbox->ensureToStringAllowed(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
|
||||
/**
|
||||
* Represents a deprecated node.
|
||||
*
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
class DeprecatedNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$expr = $this->getNode('expr');
|
||||
|
||||
if ($expr instanceof ConstantExpression) {
|
||||
$compiler->write('@trigger_error(')
|
||||
->subcompile($expr);
|
||||
} else {
|
||||
$varName = $compiler->getVarName();
|
||||
$compiler->write(sprintf('$%s = ', $varName))
|
||||
->subcompile($expr)
|
||||
->raw(";\n")
|
||||
->write(sprintf('@trigger_error($%s', $varName));
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('.')
|
||||
->string(sprintf(' ("%s" at line %d).', $this->getTemplateName(), $this->getTemplateLine()))
|
||||
->raw(", E_USER_DEPRECATED);\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\DeprecatedNode', 'Twig_Node_Deprecated');
|
@ -1,40 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
|
||||
/**
|
||||
* Represents a do node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class DoNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\DoNode', 'Twig_Node_Do');
|
@ -1,52 +0,0 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
|
||||
/**
|
||||
* Represents an embed node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EmbedNode extends IncludeNode
|
||||
{
|
||||
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
|
||||
public function __construct($name, $index, AbstractExpression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(new ConstantExpression('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
// to be removed in 2.0, used name instead
|
||||
$this->setAttribute('filename', $name);
|
||||
$this->setAttribute('index', $index);
|
||||
}
|
||||
|
||||
protected function addGetTemplate(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write('$this->loadTemplate(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(', ')
|
||||
->repr($this->getTemplateName())
|
||||
->raw(', ')
|
||||
->repr($this->getTemplateLine())
|
||||
->raw(', ')
|
||||
->string($this->getAttribute('index'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\EmbedNode', 'Twig_Node_Embed');
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Abstract class for all nodes that represents an expression.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractExpression extends Node
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\Expression\AbstractExpression', 'Twig_Node_Expression');
|
@ -1,88 +0,0 @@
|
||||
<?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\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
class ArrayExpression extends AbstractExpression
|
||||
{
|
||||
protected $index;
|
||||
|
||||
public function __construct(array $elements, $lineno)
|
||||
{
|
||||
parent::__construct($elements, [], $lineno);
|
||||
|
||||
$this->index = -1;
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if ($pair['key'] instanceof ConstantExpression && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
|
||||
$this->index = $pair['key']->getAttribute('value');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getKeyValuePairs()
|
||||
{
|
||||
$pairs = [];
|
||||
|
||||
foreach (array_chunk($this->nodes, 2) as $pair) {
|
||||
$pairs[] = [
|
||||
'key' => $pair[0],
|
||||
'value' => $pair[1],
|
||||
];
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
public function hasElement(AbstractExpression $key)
|
||||
{
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
// we compare the string representation of the keys
|
||||
// to avoid comparing the line numbers which are not relevant here.
|
||||
if ((string) $key === (string) $pair['key']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addElement(AbstractExpression $value, AbstractExpression $key = null)
|
||||
{
|
||||
if (null === $key) {
|
||||
$key = new ConstantExpression(++$this->index, $value->getTemplateLine());
|
||||
}
|
||||
|
||||
array_push($this->nodes, $key, $value);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('[');
|
||||
$first = true;
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$compiler
|
||||
->subcompile($pair['key'])
|
||||
->raw(' => ')
|
||||
->subcompile($pair['value'])
|
||||
;
|
||||
}
|
||||
$compiler->raw(']');
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\Expression\ArrayExpression', 'Twig_Node_Expression_Array');
|
@ -1,64 +0,0 @@
|
||||
<?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\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Represents an arrow function.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ArrowFunctionExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, Node $names, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(['expr' => $expr, 'names' => $names], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->raw('function (')
|
||||
;
|
||||
foreach ($this->getNode('names') as $i => $name) {
|
||||
if ($i) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('$__')
|
||||
->raw($name->getAttribute('name'))
|
||||
->raw('__')
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->raw(') use ($context) { ')
|
||||
;
|
||||
foreach ($this->getNode('names') as $name) {
|
||||
$compiler
|
||||
->raw('$context["')
|
||||
->raw($name->getAttribute('name'))
|
||||
->raw('"] = $__')
|
||||
->raw($name->getAttribute('name'))
|
||||
->raw('__; ')
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->raw('return ')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw('; }')
|
||||
;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
class AssignNameExpression extends NameExpression
|
||||
{
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('$context[')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(']')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\Expression\AssignNameExpression', 'Twig_Node_Expression_AssignName');
|