From 2fc2807a6be0bae49da81bf1c7355317455f40dd Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 9 Aug 2024 17:28:32 +0900 Subject: [PATCH 01/10] Smarty v5 update test, initial code setup --- composer.json | 19 +- src/Autoloader.php | 111 - src/BlockHandler/Base.php | 19 + src/BlockHandler/BlockHandlerInterface.php | 10 + src/BlockHandler/BlockPluginWrapper.php | 19 + src/BlockHandler/T.php | 225 ++ src/BlockHandler/TextFormat.php | 110 + src/Cacheresource/Base.php | 156 + .../Custom.php} | 150 +- src/Cacheresource/File.php | 338 ++ .../KeyValueStore.php} | 113 +- src/Compile/Base.php | 233 ++ src/Compile/BlockCompiler.php | 228 ++ src/Compile/CompilerInterface.php | 26 + src/Compile/DefaultHandlerBlockCompiler.php | 29 + .../DefaultHandlerFunctionCallCompiler.php | 47 + src/Compile/FunctionCallCompiler.php | 79 + src/Compile/Modifier/BCPluginWrapper.php | 19 + src/Compile/Modifier/Base.php | 49 + src/Compile/Modifier/CatModifierCompiler.php | 25 + .../CountCharactersModifierCompiler.php | 21 + .../CountParagraphsModifierCompiler.php | 19 + .../CountSentencesModifierCompiler.php | 19 + .../Modifier/CountWordsModifierCompiler.php | 20 + .../Modifier/DefaultModifierCompiler.php | 26 + .../Modifier/EmptyModifierCompiler.php | 19 + .../Modifier/EscapeModifierCompiler.php | 66 + .../Modifier/FromCharsetModifierCompiler.php | 21 + .../Modifier/IndentModifierCompiler.php | 24 + .../Modifier/IsArrayModifierCompiler.php | 19 + .../Modifier/IssetModifierCompiler.php | 25 + .../Modifier/JsonEncodeModifierCompiler.php | 14 + .../Modifier/LowerModifierCompiler.php | 19 + .../Modifier/ModifierCompilerInterface.php | 17 + .../Modifier/Nl2brModifierCompiler.php | 17 + .../Modifier/NoPrintModifierCompiler.php | 18 + src/Compile/Modifier/RawModifierCompiler.php | 21 + .../Modifier/RoundModifierCompiler.php | 18 + .../Modifier/StrRepeatModifierCompiler.php | 17 + .../Modifier/StringFormatModifierCompiler.php | 18 + .../Modifier/StripModifierCompiler.php | 24 + .../Modifier/StripTagsModifierCompiler.php | 22 + .../Modifier/StrlenModifierCompiler.php | 18 + .../Modifier/SubstrModifierCompiler.php | 15 + .../Modifier/ToCharsetModifierCompiler.php | 21 + .../Modifier/UnescapeModifierCompiler.php | 34 + .../Modifier/UpperModifierCompiler.php | 18 + .../Modifier/WordWrapModifierCompiler.php | 27 + src/Compile/ModifierCompiler.php | 95 + src/Compile/ObjectMethodBlockCompiler.php | 44 + src/Compile/ObjectMethodCallCompiler.php | 76 + src/Compile/PrintExpressionCompiler.php | 96 + src/Compile/SpecialVariableCompiler.php | 134 + .../Tag/Append.php} | 34 +- src/Compile/Tag/Assign.php | 95 + src/Compile/Tag/BCPluginWrapper.php | 31 + src/Compile/Tag/Block.php | 92 + src/Compile/Tag/BlockClose.php | 110 + src/Compile/Tag/BreakTag.php | 123 + src/Compile/Tag/Call.php | 81 + src/Compile/Tag/Capture.php | 72 + src/Compile/Tag/CaptureClose.php | 43 + src/Compile/Tag/ConfigLoad.php | 77 + src/Compile/Tag/ContinueTag.php | 27 + src/Compile/Tag/Debug.php | 45 + src/Compile/Tag/ElseIfTag.php | 86 + src/Compile/Tag/ElseTag.php | 29 + src/Compile/Tag/EvalTag.php | 74 + src/Compile/Tag/ExtendsTag.php | 87 + src/Compile/Tag/ForClose.php | 51 + src/Compile/Tag/ForElse.php | 30 + src/Compile/Tag/ForTag.php | 101 + src/Compile/Tag/ForeachClose.php | 55 + src/Compile/Tag/ForeachElse.php | 35 + src/Compile/Tag/ForeachSection.php | 206 ++ src/Compile/Tag/ForeachTag.php | 286 ++ src/Compile/Tag/FunctionClose.php | 164 + src/Compile/Tag/FunctionTag.php | 73 + src/Compile/Tag/IfClose.php | 48 + src/Compile/Tag/IfTag.php | 70 + src/Compile/Tag/IncludeTag.php | 189 + .../Tag/Inheritance.php} | 27 +- src/Compile/Tag/Ldelim.php | 41 + src/Compile/Tag/Nocache.php | 36 + src/Compile/Tag/NocacheClose.php | 37 + src/Compile/Tag/Rdelim.php | 36 + src/Compile/Tag/Section.php | 399 +++ src/Compile/Tag/SectionClose.php | 48 + src/Compile/Tag/SectionElse.php | 29 + src/Compile/Tag/Setfilter.php | 40 + src/Compile/Tag/SetfilterClose.php | 43 + src/Compile/Tag/WhileClose.php | 45 + src/Compile/Tag/WhileTag.php | 72 + src/Compiler/BaseCompiler.php | 23 + src/Compiler/CodeFrame.php | 126 + src/Compiler/Configfile.php | 173 + src/Compiler/Template.php | 1512 ++++++++ src/CompilerException.php | 73 + src/Data.php | 521 +++ .../smarty_internal_debug.php => Debug.php} | 212 +- ...rnal_errorhandler.php => ErrorHandler.php} | 23 +- src/Exception.php | 16 + src/Extension/BCPluginsAdapter.php | 229 ++ src/Extension/Base.php | 41 + src/Extension/CallbackWrapper.php | 35 + src/Extension/CoreExtension.php | 49 + src/Extension/DefaultExtension.php | 760 ++++ src/Extension/ExtensionInterface.php | 83 + src/Filter/FilterInterface.php | 9 + src/Filter/FilterPluginWrapper.php | 15 + src/Filter/Output/TrimWhitespace.php | 91 + src/FunctionHandler/BCPluginWrapper.php | 21 + src/FunctionHandler/Base.php | 21 + src/FunctionHandler/Count.php | 36 + src/FunctionHandler/Counter.php | 61 + src/FunctionHandler/Cycle.php | 90 + src/FunctionHandler/Fetch.php | 203 ++ .../FunctionHandlerInterface.php | 10 + src/FunctionHandler/HtmlBase.php | 109 + src/FunctionHandler/HtmlCheckboxes.php | 198 ++ src/FunctionHandler/HtmlImage.php | 149 + src/FunctionHandler/HtmlOptions.php | 236 ++ src/FunctionHandler/HtmlRadios.php | 174 + src/FunctionHandler/HtmlSelectDate.php | 381 ++ src/FunctionHandler/HtmlSelectTime.php | 334 ++ src/FunctionHandler/HtmlTable.php | 161 + src/FunctionHandler/Mailto.php | 141 + src/FunctionHandler/Math.php | 140 + src/FunctionHandler/Popup.php | 123 + src/FunctionHandler/PopupInit.php | 36 + .../ConfigfileLexer.php} | 568 ++- src/Lexer/ConfigfileLexer.plex | 321 ++ .../TemplateLexer.php} | 240 +- src/Lexer/TemplateLexer.plex | 677 ++++ src/ParseTree/Base.php | 45 + .../Code.php} | 17 +- .../Dq.php} | 54 +- .../DqContent.php} | 20 +- .../Tag.php} | 29 +- .../Template.php} | 43 +- .../Text.php} | 16 +- src/Parser/ConfigfileParser.php | 972 ++++++ src/Parser/ConfigfileParser.y | 352 ++ src/Parser/TemplateParser.php | 3051 +++++++++++++++++ src/Parser/TemplateParser.y | 1312 +++++++ src/Resource/BasePlugin.php | 145 + src/Resource/CustomPlugin.php | 105 + .../ExtendsPlugin.php} | 72 +- src/Resource/FilePlugin.php | 180 + src/Resource/RecompiledPlugin.php | 50 + src/Resource/StreamPlugin.php | 71 + .../StringEval.php} | 49 +- src/Resource/StringPlugin.php | 94 + .../Block.php} | 18 +- src/Runtime/CaptureRuntime.php | 163 + src/Runtime/DefaultPluginHandlerRuntime.php | 73 + src/Runtime/ForeachRuntime.php | 160 + src/Runtime/InheritanceRuntime.php | 243 ++ src/Runtime/TplFunctionRuntime.php | 144 + src/Security.php | 560 +++ src/Smarty.class.php | 1405 -------- src/Smarty.php | 2240 ++++++++++++ src/Template.php | 732 ++++ src/Template/Cached.php | 428 +++ src/Template/Compiled.php | 302 ++ src/Template/Config.php | 36 + src/Template/GeneratedPhpFile.php | 159 + src/Template/Source.php | 285 ++ src/TemplateBase.php | 425 +++ src/TestInstall.php | 211 ++ src/UndefinedVariable.php | 19 + src/Variable.php | 118 + src/bootstrap.php | 16 - src/debug.tpl | 7 +- src/functions.php | 212 +- src/plugins/block.textformat.php | 121 - src/plugins/function.counter.php | 62 - src/plugins/function.cycle.php | 92 - src/plugins/function.fetch.php | 204 -- src/plugins/function.html_checkboxes.php | 296 -- src/plugins/function.html_image.php | 158 - src/plugins/function.html_options.php | 260 -- src/plugins/function.html_radios.php | 266 -- src/plugins/function.html_select_date.php | 395 --- src/plugins/function.html_select_time.php | 354 -- src/plugins/function.html_table.php | 164 - src/plugins/function.mailto.php | 142 - src/plugins/function.math.php | 142 - src/plugins/function.popup.php | 119 - src/plugins/function.popup_init.php | 40 - src/plugins/modifier.capitalize.php | 147 - src/plugins/modifier.count.php | 36 - src/plugins/modifier.date_format.php | 86 - src/plugins/modifier.debug_print_var.php | 103 - src/plugins/modifier.escape.php | 189 - src/plugins/modifier.explode.php | 25 - src/plugins/modifier.implode.php | 15 - src/plugins/modifier.mb_wordwrap.php | 71 - src/plugins/modifier.number_format.php | 26 - src/plugins/modifier.regex_replace.php | 55 - src/plugins/modifier.replace.php | 37 - src/plugins/modifier.spacify.php | 26 - src/plugins/modifier.truncate.php | 62 - src/plugins/modifiercompiler.cat.php | 28 - .../modifiercompiler.count_characters.php | 32 - .../modifiercompiler.count_paragraphs.php | 26 - .../modifiercompiler.count_sentences.php | 26 - src/plugins/modifiercompiler.count_words.php | 31 - src/plugins/modifiercompiler.default.php | 32 - src/plugins/modifiercompiler.escape.php | 87 - src/plugins/modifiercompiler.from_charset.php | 30 - src/plugins/modifiercompiler.indent.php | 30 - src/plugins/modifiercompiler.json_encode.php | 11 - src/plugins/modifiercompiler.lower.php | 29 - src/plugins/modifiercompiler.nl2br.php | 23 - src/plugins/modifiercompiler.noprint.php | 20 - src/plugins/modifiercompiler.round.php | 23 - src/plugins/modifiercompiler.str_repeat.php | 23 - .../modifiercompiler.string_format.php | 24 - src/plugins/modifiercompiler.strip.php | 30 - src/plugins/modifiercompiler.strip_tags.php | 28 - src/plugins/modifiercompiler.strlen.php | 23 - src/plugins/modifiercompiler.substr.php | 12 - src/plugins/modifiercompiler.to_charset.php | 30 - src/plugins/modifiercompiler.unescape.php | 52 - src/plugins/modifiercompiler.upper.php | 28 - src/plugins/modifiercompiler.wordwrap.php | 39 - src/plugins/outputfilter.trimwhitespace.php | 89 - src/plugins/shared.escape_special_chars.php | 26 - src/plugins/shared.literal_compiler_param.php | 35 - src/plugins/shared.make_timestamp.php | 49 - src/plugins/shared.mb_str_replace.php | 87 - src/plugins/shared.mb_unicode.php | 51 - .../variablefilter.htmlspecialchars.php | 19 - src/sysplugins/smarty_cacheresource.php | 219 -- src/sysplugins/smarty_data.php | 68 - .../smarty_internal_cacheresource_file.php | 235 -- .../smarty_internal_compile_assign.php | 96 - .../smarty_internal_compile_block.php | 189 - .../smarty_internal_compile_block_child.php | 24 - .../smarty_internal_compile_block_parent.php | 31 - .../smarty_internal_compile_break.php | 117 - .../smarty_internal_compile_call.php | 89 - .../smarty_internal_compile_capture.php | 105 - .../smarty_internal_compile_child.php | 79 - .../smarty_internal_compile_config_load.php | 96 - .../smarty_internal_compile_continue.php | 25 - .../smarty_internal_compile_debug.php | 40 - .../smarty_internal_compile_eval.php | 70 - .../smarty_internal_compile_extends.php | 158 - .../smarty_internal_compile_for.php | 164 - .../smarty_internal_compile_foreach.php | 343 -- .../smarty_internal_compile_function.php | 236 -- src/sysplugins/smarty_internal_compile_if.php | 207 -- .../smarty_internal_compile_include.php | 347 -- .../smarty_internal_compile_insert.php | 157 - .../smarty_internal_compile_ldelim.php | 37 - .../smarty_internal_compile_make_nocache.php | 62 - .../smarty_internal_compile_nocache.php | 73 - .../smarty_internal_compile_parent.php | 31 - ..._internal_compile_private_block_plugin.php | 124 - ...nternal_compile_private_foreachsection.php | 228 -- ...ternal_compile_private_function_plugin.php | 78 - ...arty_internal_compile_private_modifier.php | 163 - ..._compile_private_object_block_function.php | 42 - ...ternal_compile_private_object_function.php | 85 - ...ernal_compile_private_print_expression.php | 161 - ...ernal_compile_private_registered_block.php | 72 - ...al_compile_private_registered_function.php | 91 - ...ernal_compile_private_special_variable.php | 130 - .../smarty_internal_compile_rdelim.php | 34 - .../smarty_internal_compile_section.php | 462 --- .../smarty_internal_compile_setfilter.php | 68 - .../smarty_internal_compile_while.php | 100 - .../smarty_internal_compilebase.php | 203 -- .../smarty_internal_config_file_compiler.php | 211 -- .../smarty_internal_configfileparser.php | 1046 ------ src/sysplugins/smarty_internal_data.php | 272 -- .../smarty_internal_extension_handler.php | 197 -- ...rty_internal_method_addautoloadfilters.php | 53 - ...ty_internal_method_adddefaultmodifiers.php | 42 - .../smarty_internal_method_append.php | 74 - .../smarty_internal_method_appendbyref.php | 49 - .../smarty_internal_method_assignbyref.php | 36 - .../smarty_internal_method_assignglobal.php | 44 - .../smarty_internal_method_clearallassign.php | 36 - .../smarty_internal_method_clearallcache.php | 41 - .../smarty_internal_method_clearassign.php | 43 - .../smarty_internal_method_clearcache.php | 50 - ..._internal_method_clearcompiledtemplate.php | 131 - .../smarty_internal_method_clearconfig.php | 41 - ...marty_internal_method_compileallconfig.php | 36 - ...ty_internal_method_compilealltemplates.php | 130 - .../smarty_internal_method_configload.php | 182 - .../smarty_internal_method_createdata.php | 44 - ...rty_internal_method_getautoloadfilters.php | 37 - ...arty_internal_method_getconfigvariable.php | 34 - .../smarty_internal_method_getconfigvars.php | 58 - ...marty_internal_method_getdebugtemplate.php | 35 - ...ty_internal_method_getdefaultmodifiers.php | 35 - .../smarty_internal_method_getglobal.php | 47 - ...ty_internal_method_getregisteredobject.php | 44 - ...arty_internal_method_getstreamvariable.php | 50 - .../smarty_internal_method_gettags.php | 63 - ...smarty_internal_method_gettemplatevars.php | 119 - .../smarty_internal_method_literals.php | 100 - .../smarty_internal_method_loadfilter.php | 77 - .../smarty_internal_method_loadplugin.php | 111 - .../smarty_internal_method_mustcompile.php | 50 - ..._internal_method_registercacheresource.php | 42 - .../smarty_internal_method_registerclass.php | 46 - ...al_method_registerdefaultconfighandler.php | 42 - ...al_method_registerdefaultpluginhandler.php | 43 - ..._method_registerdefaulttemplatehandler.php | 88 - .../smarty_internal_method_registerfilter.php | 87 - .../smarty_internal_method_registerobject.php | 84 - .../smarty_internal_method_registerplugin.php | 58 - ...marty_internal_method_registerresource.php | 39 - ...rty_internal_method_setautoloadfilters.php | 72 - ...marty_internal_method_setdebugtemplate.php | 41 - ...ty_internal_method_setdefaultmodifiers.php | 38 - .../smarty_internal_method_unloadfilter.php | 43 - ...nternal_method_unregistercacheresource.php | 40 - ...marty_internal_method_unregisterfilter.php | 43 - ...marty_internal_method_unregisterobject.php | 40 - ...marty_internal_method_unregisterplugin.php | 41 - ...rty_internal_method_unregisterresource.php | 40 - .../smarty_internal_nocache_insert.php | 51 - src/sysplugins/smarty_internal_parsetree.php | 50 - .../smarty_internal_resource_file.php | 180 - .../smarty_internal_resource_php.php | 116 - .../smarty_internal_resource_stream.php | 78 - .../smarty_internal_resource_string.php | 108 - .../smarty_internal_runtime_cachemodify.php | 68 - ...rty_internal_runtime_cacheresourcefile.php | 139 - .../smarty_internal_runtime_capture.php | 174 - .../smarty_internal_runtime_codeframe.php | 103 - .../smarty_internal_runtime_filterhandler.php | 69 - .../smarty_internal_runtime_foreach.php | 162 - ...smarty_internal_runtime_getincludepath.php | 181 - .../smarty_internal_runtime_inheritance.php | 251 -- .../smarty_internal_runtime_make_nocache.php | 54 - .../smarty_internal_runtime_tplfunction.php | 177 - .../smarty_internal_runtime_updatecache.php | 183 - .../smarty_internal_runtime_updatescope.php | 115 - .../smarty_internal_runtime_writefile.php | 91 - ...smarty_internal_smartytemplatecompiler.php | 184 - src/sysplugins/smarty_internal_template.php | 741 ---- .../smarty_internal_templatebase.php | 401 --- .../smarty_internal_templatecompilerbase.php | 1760 ---------- .../smarty_internal_templateparser.php | 2929 ---------------- .../smarty_internal_testinstall.php | 605 ---- src/sysplugins/smarty_internal_undefined.php | 67 - src/sysplugins/smarty_resource.php | 260 -- src/sysplugins/smarty_resource_custom.php | 104 - src/sysplugins/smarty_resource_recompiled.php | 94 - src/sysplugins/smarty_resource_uncompiled.php | 49 - src/sysplugins/smarty_security.php | 680 ---- src/sysplugins/smarty_template_cached.php | 257 -- src/sysplugins/smarty_template_compiled.php | 257 -- src/sysplugins/smarty_template_config.php | 100 - .../smarty_template_resource_base.php | 152 - src/sysplugins/smarty_template_source.php | 213 -- src/sysplugins/smarty_undefined_variable.php | 33 - src/sysplugins/smarty_variable.php | 48 - src/sysplugins/smartycompilerexception.php | 73 - src/sysplugins/smartyexception.php | 19 - test/cache/.gitignore | 2 + test/includes/locale/ja_JP.frontend.UTF-8.po | 27 + .../locale/ja_JP/LC_MESSAGES/frontend.mo | Bin 0 -> 474 bytes .../includes/templates/frontend/test.full.tpl | 95 + .../templates/frontend/test.simple.tpl | 90 + test/index.php | 124 + test/js/overlib/overlib.js | 1491 ++++++++ {src => test}/plugins/block.t.php | 61 +- test/plugins/function.popup.php | 124 + test/plugins/function.popup_init.php | 44 + test/src/index.php | 136 + test/src/overlib.js | 1491 ++++++++ test/src/translate.php | 50 + test/templates_c/.gitignore | 2 + test/tmp/.gitignore | 2 + 382 files changed, 30655 insertions(+), 28498 deletions(-) delete mode 100644 src/Autoloader.php create mode 100644 src/BlockHandler/Base.php create mode 100644 src/BlockHandler/BlockHandlerInterface.php create mode 100644 src/BlockHandler/BlockPluginWrapper.php create mode 100644 src/BlockHandler/T.php create mode 100644 src/BlockHandler/TextFormat.php create mode 100644 src/Cacheresource/Base.php rename src/{sysplugins/smarty_cacheresource_custom.php => Cacheresource/Custom.php} (64%) create mode 100644 src/Cacheresource/File.php rename src/{sysplugins/smarty_cacheresource_keyvaluestore.php => Cacheresource/KeyValueStore.php} (83%) create mode 100644 src/Compile/Base.php create mode 100644 src/Compile/BlockCompiler.php create mode 100644 src/Compile/CompilerInterface.php create mode 100644 src/Compile/DefaultHandlerBlockCompiler.php create mode 100644 src/Compile/DefaultHandlerFunctionCallCompiler.php create mode 100644 src/Compile/FunctionCallCompiler.php create mode 100644 src/Compile/Modifier/BCPluginWrapper.php create mode 100644 src/Compile/Modifier/Base.php create mode 100644 src/Compile/Modifier/CatModifierCompiler.php create mode 100644 src/Compile/Modifier/CountCharactersModifierCompiler.php create mode 100644 src/Compile/Modifier/CountParagraphsModifierCompiler.php create mode 100644 src/Compile/Modifier/CountSentencesModifierCompiler.php create mode 100644 src/Compile/Modifier/CountWordsModifierCompiler.php create mode 100644 src/Compile/Modifier/DefaultModifierCompiler.php create mode 100644 src/Compile/Modifier/EmptyModifierCompiler.php create mode 100644 src/Compile/Modifier/EscapeModifierCompiler.php create mode 100644 src/Compile/Modifier/FromCharsetModifierCompiler.php create mode 100644 src/Compile/Modifier/IndentModifierCompiler.php create mode 100644 src/Compile/Modifier/IsArrayModifierCompiler.php create mode 100644 src/Compile/Modifier/IssetModifierCompiler.php create mode 100644 src/Compile/Modifier/JsonEncodeModifierCompiler.php create mode 100644 src/Compile/Modifier/LowerModifierCompiler.php create mode 100644 src/Compile/Modifier/ModifierCompilerInterface.php create mode 100644 src/Compile/Modifier/Nl2brModifierCompiler.php create mode 100644 src/Compile/Modifier/NoPrintModifierCompiler.php create mode 100644 src/Compile/Modifier/RawModifierCompiler.php create mode 100644 src/Compile/Modifier/RoundModifierCompiler.php create mode 100644 src/Compile/Modifier/StrRepeatModifierCompiler.php create mode 100644 src/Compile/Modifier/StringFormatModifierCompiler.php create mode 100644 src/Compile/Modifier/StripModifierCompiler.php create mode 100644 src/Compile/Modifier/StripTagsModifierCompiler.php create mode 100644 src/Compile/Modifier/StrlenModifierCompiler.php create mode 100644 src/Compile/Modifier/SubstrModifierCompiler.php create mode 100644 src/Compile/Modifier/ToCharsetModifierCompiler.php create mode 100644 src/Compile/Modifier/UnescapeModifierCompiler.php create mode 100644 src/Compile/Modifier/UpperModifierCompiler.php create mode 100644 src/Compile/Modifier/WordWrapModifierCompiler.php create mode 100644 src/Compile/ModifierCompiler.php create mode 100644 src/Compile/ObjectMethodBlockCompiler.php create mode 100644 src/Compile/ObjectMethodCallCompiler.php create mode 100644 src/Compile/PrintExpressionCompiler.php create mode 100644 src/Compile/SpecialVariableCompiler.php rename src/{sysplugins/smarty_internal_compile_append.php => Compile/Tag/Append.php} (60%) create mode 100644 src/Compile/Tag/Assign.php create mode 100644 src/Compile/Tag/BCPluginWrapper.php create mode 100644 src/Compile/Tag/Block.php create mode 100644 src/Compile/Tag/BlockClose.php create mode 100644 src/Compile/Tag/BreakTag.php create mode 100644 src/Compile/Tag/Call.php create mode 100644 src/Compile/Tag/Capture.php create mode 100644 src/Compile/Tag/CaptureClose.php create mode 100644 src/Compile/Tag/ConfigLoad.php create mode 100644 src/Compile/Tag/ContinueTag.php create mode 100644 src/Compile/Tag/Debug.php create mode 100644 src/Compile/Tag/ElseIfTag.php create mode 100644 src/Compile/Tag/ElseTag.php create mode 100644 src/Compile/Tag/EvalTag.php create mode 100644 src/Compile/Tag/ExtendsTag.php create mode 100644 src/Compile/Tag/ForClose.php create mode 100644 src/Compile/Tag/ForElse.php create mode 100644 src/Compile/Tag/ForTag.php create mode 100644 src/Compile/Tag/ForeachClose.php create mode 100644 src/Compile/Tag/ForeachElse.php create mode 100644 src/Compile/Tag/ForeachSection.php create mode 100644 src/Compile/Tag/ForeachTag.php create mode 100644 src/Compile/Tag/FunctionClose.php create mode 100644 src/Compile/Tag/FunctionTag.php create mode 100644 src/Compile/Tag/IfClose.php create mode 100644 src/Compile/Tag/IfTag.php create mode 100644 src/Compile/Tag/IncludeTag.php rename src/{sysplugins/smarty_internal_compile_shared_inheritance.php => Compile/Tag/Inheritance.php} (58%) create mode 100644 src/Compile/Tag/Ldelim.php create mode 100644 src/Compile/Tag/Nocache.php create mode 100644 src/Compile/Tag/NocacheClose.php create mode 100644 src/Compile/Tag/Rdelim.php create mode 100644 src/Compile/Tag/Section.php create mode 100644 src/Compile/Tag/SectionClose.php create mode 100644 src/Compile/Tag/SectionElse.php create mode 100644 src/Compile/Tag/Setfilter.php create mode 100644 src/Compile/Tag/SetfilterClose.php create mode 100644 src/Compile/Tag/WhileClose.php create mode 100644 src/Compile/Tag/WhileTag.php create mode 100644 src/Compiler/BaseCompiler.php create mode 100644 src/Compiler/CodeFrame.php create mode 100644 src/Compiler/Configfile.php create mode 100644 src/Compiler/Template.php create mode 100644 src/CompilerException.php create mode 100644 src/Data.php rename src/{sysplugins/smarty_internal_debug.php => Debug.php} (55%) rename src/{sysplugins/smarty_internal_errorhandler.php => ErrorHandler.php} (86%) create mode 100644 src/Exception.php create mode 100644 src/Extension/BCPluginsAdapter.php create mode 100644 src/Extension/Base.php create mode 100644 src/Extension/CallbackWrapper.php create mode 100644 src/Extension/CoreExtension.php create mode 100644 src/Extension/DefaultExtension.php create mode 100644 src/Extension/ExtensionInterface.php create mode 100644 src/Filter/FilterInterface.php create mode 100644 src/Filter/FilterPluginWrapper.php create mode 100644 src/Filter/Output/TrimWhitespace.php create mode 100644 src/FunctionHandler/BCPluginWrapper.php create mode 100644 src/FunctionHandler/Base.php create mode 100644 src/FunctionHandler/Count.php create mode 100644 src/FunctionHandler/Counter.php create mode 100644 src/FunctionHandler/Cycle.php create mode 100644 src/FunctionHandler/Fetch.php create mode 100644 src/FunctionHandler/FunctionHandlerInterface.php create mode 100644 src/FunctionHandler/HtmlBase.php create mode 100644 src/FunctionHandler/HtmlCheckboxes.php create mode 100644 src/FunctionHandler/HtmlImage.php create mode 100644 src/FunctionHandler/HtmlOptions.php create mode 100644 src/FunctionHandler/HtmlRadios.php create mode 100644 src/FunctionHandler/HtmlSelectDate.php create mode 100644 src/FunctionHandler/HtmlSelectTime.php create mode 100644 src/FunctionHandler/HtmlTable.php create mode 100644 src/FunctionHandler/Mailto.php create mode 100644 src/FunctionHandler/Math.php create mode 100644 src/FunctionHandler/Popup.php create mode 100644 src/FunctionHandler/PopupInit.php rename src/{sysplugins/smarty_internal_configfilelexer.php => Lexer/ConfigfileLexer.php} (57%) create mode 100644 src/Lexer/ConfigfileLexer.plex rename src/{sysplugins/smarty_internal_templatelexer.php => Lexer/TemplateLexer.php} (79%) create mode 100644 src/Lexer/TemplateLexer.plex create mode 100644 src/ParseTree/Base.php rename src/{sysplugins/smarty_internal_parsetree_code.php => ParseTree/Code.php} (67%) rename src/{sysplugins/smarty_internal_parsetree_dq.php => ParseTree/Dq.php} (50%) rename src/{sysplugins/smarty_internal_parsetree_dqcontent.php => ParseTree/DqContent.php} (58%) rename src/{sysplugins/smarty_internal_parsetree_tag.php => ParseTree/Tag.php} (63%) rename src/{sysplugins/smarty_internal_parsetree_template.php => ParseTree/Template.php} (77%) rename src/{sysplugins/smarty_internal_parsetree_text.php => ParseTree/Text.php} (78%) create mode 100644 src/Parser/ConfigfileParser.php create mode 100644 src/Parser/ConfigfileParser.y create mode 100644 src/Parser/TemplateParser.php create mode 100644 src/Parser/TemplateParser.y create mode 100644 src/Resource/BasePlugin.php create mode 100644 src/Resource/CustomPlugin.php rename src/{sysplugins/smarty_internal_resource_extends.php => Resource/ExtendsPlugin.php} (50%) create mode 100644 src/Resource/FilePlugin.php create mode 100644 src/Resource/RecompiledPlugin.php create mode 100644 src/Resource/StreamPlugin.php rename src/{sysplugins/smarty_internal_resource_eval.php => Resource/StringEval.php} (53%) create mode 100644 src/Resource/StringPlugin.php rename src/{sysplugins/smarty_internal_block.php => Runtime/Block.php} (81%) create mode 100644 src/Runtime/CaptureRuntime.php create mode 100644 src/Runtime/DefaultPluginHandlerRuntime.php create mode 100644 src/Runtime/ForeachRuntime.php create mode 100644 src/Runtime/InheritanceRuntime.php create mode 100644 src/Runtime/TplFunctionRuntime.php create mode 100644 src/Security.php delete mode 100644 src/Smarty.class.php create mode 100644 src/Smarty.php create mode 100644 src/Template.php create mode 100644 src/Template/Cached.php create mode 100644 src/Template/Compiled.php create mode 100644 src/Template/Config.php create mode 100644 src/Template/GeneratedPhpFile.php create mode 100644 src/Template/Source.php create mode 100644 src/TemplateBase.php create mode 100644 src/TestInstall.php create mode 100644 src/UndefinedVariable.php create mode 100644 src/Variable.php delete mode 100644 src/bootstrap.php delete mode 100644 src/plugins/block.textformat.php delete mode 100644 src/plugins/function.counter.php delete mode 100644 src/plugins/function.cycle.php delete mode 100644 src/plugins/function.fetch.php delete mode 100644 src/plugins/function.html_checkboxes.php delete mode 100644 src/plugins/function.html_image.php delete mode 100644 src/plugins/function.html_options.php delete mode 100644 src/plugins/function.html_radios.php delete mode 100644 src/plugins/function.html_select_date.php delete mode 100644 src/plugins/function.html_select_time.php delete mode 100644 src/plugins/function.html_table.php delete mode 100644 src/plugins/function.mailto.php delete mode 100644 src/plugins/function.math.php delete mode 100644 src/plugins/function.popup.php delete mode 100644 src/plugins/function.popup_init.php delete mode 100644 src/plugins/modifier.capitalize.php delete mode 100644 src/plugins/modifier.count.php delete mode 100644 src/plugins/modifier.date_format.php delete mode 100644 src/plugins/modifier.debug_print_var.php delete mode 100644 src/plugins/modifier.escape.php delete mode 100644 src/plugins/modifier.explode.php delete mode 100644 src/plugins/modifier.implode.php delete mode 100644 src/plugins/modifier.mb_wordwrap.php delete mode 100644 src/plugins/modifier.number_format.php delete mode 100644 src/plugins/modifier.regex_replace.php delete mode 100644 src/plugins/modifier.replace.php delete mode 100644 src/plugins/modifier.spacify.php delete mode 100644 src/plugins/modifier.truncate.php delete mode 100644 src/plugins/modifiercompiler.cat.php delete mode 100644 src/plugins/modifiercompiler.count_characters.php delete mode 100644 src/plugins/modifiercompiler.count_paragraphs.php delete mode 100644 src/plugins/modifiercompiler.count_sentences.php delete mode 100644 src/plugins/modifiercompiler.count_words.php delete mode 100644 src/plugins/modifiercompiler.default.php delete mode 100644 src/plugins/modifiercompiler.escape.php delete mode 100644 src/plugins/modifiercompiler.from_charset.php delete mode 100644 src/plugins/modifiercompiler.indent.php delete mode 100644 src/plugins/modifiercompiler.json_encode.php delete mode 100644 src/plugins/modifiercompiler.lower.php delete mode 100644 src/plugins/modifiercompiler.nl2br.php delete mode 100644 src/plugins/modifiercompiler.noprint.php delete mode 100644 src/plugins/modifiercompiler.round.php delete mode 100644 src/plugins/modifiercompiler.str_repeat.php delete mode 100644 src/plugins/modifiercompiler.string_format.php delete mode 100644 src/plugins/modifiercompiler.strip.php delete mode 100644 src/plugins/modifiercompiler.strip_tags.php delete mode 100644 src/plugins/modifiercompiler.strlen.php delete mode 100644 src/plugins/modifiercompiler.substr.php delete mode 100644 src/plugins/modifiercompiler.to_charset.php delete mode 100644 src/plugins/modifiercompiler.unescape.php delete mode 100644 src/plugins/modifiercompiler.upper.php delete mode 100644 src/plugins/modifiercompiler.wordwrap.php delete mode 100644 src/plugins/outputfilter.trimwhitespace.php delete mode 100644 src/plugins/shared.escape_special_chars.php delete mode 100644 src/plugins/shared.literal_compiler_param.php delete mode 100644 src/plugins/shared.make_timestamp.php delete mode 100644 src/plugins/shared.mb_str_replace.php delete mode 100644 src/plugins/shared.mb_unicode.php delete mode 100644 src/plugins/variablefilter.htmlspecialchars.php delete mode 100644 src/sysplugins/smarty_cacheresource.php delete mode 100644 src/sysplugins/smarty_data.php delete mode 100644 src/sysplugins/smarty_internal_cacheresource_file.php delete mode 100644 src/sysplugins/smarty_internal_compile_assign.php delete mode 100644 src/sysplugins/smarty_internal_compile_block.php delete mode 100644 src/sysplugins/smarty_internal_compile_block_child.php delete mode 100644 src/sysplugins/smarty_internal_compile_block_parent.php delete mode 100644 src/sysplugins/smarty_internal_compile_break.php delete mode 100644 src/sysplugins/smarty_internal_compile_call.php delete mode 100644 src/sysplugins/smarty_internal_compile_capture.php delete mode 100644 src/sysplugins/smarty_internal_compile_child.php delete mode 100644 src/sysplugins/smarty_internal_compile_config_load.php delete mode 100644 src/sysplugins/smarty_internal_compile_continue.php delete mode 100644 src/sysplugins/smarty_internal_compile_debug.php delete mode 100644 src/sysplugins/smarty_internal_compile_eval.php delete mode 100644 src/sysplugins/smarty_internal_compile_extends.php delete mode 100644 src/sysplugins/smarty_internal_compile_for.php delete mode 100644 src/sysplugins/smarty_internal_compile_foreach.php delete mode 100644 src/sysplugins/smarty_internal_compile_function.php delete mode 100644 src/sysplugins/smarty_internal_compile_if.php delete mode 100644 src/sysplugins/smarty_internal_compile_include.php delete mode 100644 src/sysplugins/smarty_internal_compile_insert.php delete mode 100644 src/sysplugins/smarty_internal_compile_ldelim.php delete mode 100644 src/sysplugins/smarty_internal_compile_make_nocache.php delete mode 100644 src/sysplugins/smarty_internal_compile_nocache.php delete mode 100644 src/sysplugins/smarty_internal_compile_parent.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_block_plugin.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_foreachsection.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_function_plugin.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_modifier.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_object_block_function.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_object_function.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_print_expression.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_registered_block.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_registered_function.php delete mode 100644 src/sysplugins/smarty_internal_compile_private_special_variable.php delete mode 100644 src/sysplugins/smarty_internal_compile_rdelim.php delete mode 100644 src/sysplugins/smarty_internal_compile_section.php delete mode 100644 src/sysplugins/smarty_internal_compile_setfilter.php delete mode 100644 src/sysplugins/smarty_internal_compile_while.php delete mode 100644 src/sysplugins/smarty_internal_compilebase.php delete mode 100644 src/sysplugins/smarty_internal_config_file_compiler.php delete mode 100644 src/sysplugins/smarty_internal_configfileparser.php delete mode 100644 src/sysplugins/smarty_internal_data.php delete mode 100644 src/sysplugins/smarty_internal_extension_handler.php delete mode 100644 src/sysplugins/smarty_internal_method_addautoloadfilters.php delete mode 100644 src/sysplugins/smarty_internal_method_adddefaultmodifiers.php delete mode 100644 src/sysplugins/smarty_internal_method_append.php delete mode 100644 src/sysplugins/smarty_internal_method_appendbyref.php delete mode 100644 src/sysplugins/smarty_internal_method_assignbyref.php delete mode 100644 src/sysplugins/smarty_internal_method_assignglobal.php delete mode 100644 src/sysplugins/smarty_internal_method_clearallassign.php delete mode 100644 src/sysplugins/smarty_internal_method_clearallcache.php delete mode 100644 src/sysplugins/smarty_internal_method_clearassign.php delete mode 100644 src/sysplugins/smarty_internal_method_clearcache.php delete mode 100644 src/sysplugins/smarty_internal_method_clearcompiledtemplate.php delete mode 100644 src/sysplugins/smarty_internal_method_clearconfig.php delete mode 100644 src/sysplugins/smarty_internal_method_compileallconfig.php delete mode 100644 src/sysplugins/smarty_internal_method_compilealltemplates.php delete mode 100644 src/sysplugins/smarty_internal_method_configload.php delete mode 100644 src/sysplugins/smarty_internal_method_createdata.php delete mode 100644 src/sysplugins/smarty_internal_method_getautoloadfilters.php delete mode 100644 src/sysplugins/smarty_internal_method_getconfigvariable.php delete mode 100644 src/sysplugins/smarty_internal_method_getconfigvars.php delete mode 100644 src/sysplugins/smarty_internal_method_getdebugtemplate.php delete mode 100644 src/sysplugins/smarty_internal_method_getdefaultmodifiers.php delete mode 100644 src/sysplugins/smarty_internal_method_getglobal.php delete mode 100644 src/sysplugins/smarty_internal_method_getregisteredobject.php delete mode 100644 src/sysplugins/smarty_internal_method_getstreamvariable.php delete mode 100644 src/sysplugins/smarty_internal_method_gettags.php delete mode 100644 src/sysplugins/smarty_internal_method_gettemplatevars.php delete mode 100644 src/sysplugins/smarty_internal_method_literals.php delete mode 100644 src/sysplugins/smarty_internal_method_loadfilter.php delete mode 100644 src/sysplugins/smarty_internal_method_loadplugin.php delete mode 100644 src/sysplugins/smarty_internal_method_mustcompile.php delete mode 100644 src/sysplugins/smarty_internal_method_registercacheresource.php delete mode 100644 src/sysplugins/smarty_internal_method_registerclass.php delete mode 100644 src/sysplugins/smarty_internal_method_registerdefaultconfighandler.php delete mode 100644 src/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php delete mode 100644 src/sysplugins/smarty_internal_method_registerdefaulttemplatehandler.php delete mode 100644 src/sysplugins/smarty_internal_method_registerfilter.php delete mode 100644 src/sysplugins/smarty_internal_method_registerobject.php delete mode 100644 src/sysplugins/smarty_internal_method_registerplugin.php delete mode 100644 src/sysplugins/smarty_internal_method_registerresource.php delete mode 100644 src/sysplugins/smarty_internal_method_setautoloadfilters.php delete mode 100644 src/sysplugins/smarty_internal_method_setdebugtemplate.php delete mode 100644 src/sysplugins/smarty_internal_method_setdefaultmodifiers.php delete mode 100644 src/sysplugins/smarty_internal_method_unloadfilter.php delete mode 100644 src/sysplugins/smarty_internal_method_unregistercacheresource.php delete mode 100644 src/sysplugins/smarty_internal_method_unregisterfilter.php delete mode 100644 src/sysplugins/smarty_internal_method_unregisterobject.php delete mode 100644 src/sysplugins/smarty_internal_method_unregisterplugin.php delete mode 100644 src/sysplugins/smarty_internal_method_unregisterresource.php delete mode 100644 src/sysplugins/smarty_internal_nocache_insert.php delete mode 100644 src/sysplugins/smarty_internal_parsetree.php delete mode 100644 src/sysplugins/smarty_internal_resource_file.php delete mode 100644 src/sysplugins/smarty_internal_resource_php.php delete mode 100644 src/sysplugins/smarty_internal_resource_stream.php delete mode 100644 src/sysplugins/smarty_internal_resource_string.php delete mode 100644 src/sysplugins/smarty_internal_runtime_cachemodify.php delete mode 100644 src/sysplugins/smarty_internal_runtime_cacheresourcefile.php delete mode 100644 src/sysplugins/smarty_internal_runtime_capture.php delete mode 100644 src/sysplugins/smarty_internal_runtime_codeframe.php delete mode 100644 src/sysplugins/smarty_internal_runtime_filterhandler.php delete mode 100644 src/sysplugins/smarty_internal_runtime_foreach.php delete mode 100644 src/sysplugins/smarty_internal_runtime_getincludepath.php delete mode 100644 src/sysplugins/smarty_internal_runtime_inheritance.php delete mode 100644 src/sysplugins/smarty_internal_runtime_make_nocache.php delete mode 100644 src/sysplugins/smarty_internal_runtime_tplfunction.php delete mode 100644 src/sysplugins/smarty_internal_runtime_updatecache.php delete mode 100644 src/sysplugins/smarty_internal_runtime_updatescope.php delete mode 100644 src/sysplugins/smarty_internal_runtime_writefile.php delete mode 100644 src/sysplugins/smarty_internal_smartytemplatecompiler.php delete mode 100644 src/sysplugins/smarty_internal_template.php delete mode 100644 src/sysplugins/smarty_internal_templatebase.php delete mode 100644 src/sysplugins/smarty_internal_templatecompilerbase.php delete mode 100644 src/sysplugins/smarty_internal_templateparser.php delete mode 100644 src/sysplugins/smarty_internal_testinstall.php delete mode 100644 src/sysplugins/smarty_internal_undefined.php delete mode 100644 src/sysplugins/smarty_resource.php delete mode 100644 src/sysplugins/smarty_resource_custom.php delete mode 100644 src/sysplugins/smarty_resource_recompiled.php delete mode 100644 src/sysplugins/smarty_resource_uncompiled.php delete mode 100644 src/sysplugins/smarty_security.php delete mode 100644 src/sysplugins/smarty_template_cached.php delete mode 100644 src/sysplugins/smarty_template_compiled.php delete mode 100644 src/sysplugins/smarty_template_config.php delete mode 100644 src/sysplugins/smarty_template_resource_base.php delete mode 100644 src/sysplugins/smarty_template_source.php delete mode 100644 src/sysplugins/smarty_undefined_variable.php delete mode 100644 src/sysplugins/smarty_variable.php delete mode 100644 src/sysplugins/smartycompilerexception.php delete mode 100644 src/sysplugins/smartyexception.php create mode 100644 test/cache/.gitignore create mode 100644 test/includes/locale/ja_JP.frontend.UTF-8.po create mode 100644 test/includes/locale/ja_JP/LC_MESSAGES/frontend.mo create mode 100644 test/includes/templates/frontend/test.full.tpl create mode 100644 test/includes/templates/frontend/test.simple.tpl create mode 100644 test/index.php create mode 100644 test/js/overlib/overlib.js rename {src => test}/plugins/block.t.php (86%) create mode 100644 test/plugins/function.popup.php create mode 100644 test/plugins/function.popup_init.php create mode 100644 test/src/index.php create mode 100644 test/src/overlib.js create mode 100644 test/src/translate.php create mode 100644 test/templates_c/.gitignore create mode 100644 test/tmp/.gitignore diff --git a/composer.json b/composer.json index dfc944f..13b7c70 100644 --- a/composer.json +++ b/composer.json @@ -8,9 +8,12 @@ "homepage": "https://github.com/smarty-php/smarty/", "license": "LGPL-3.0", "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4" : { + "Smarty\\" : "src/" + } }, "authors": [ { @@ -19,5 +22,13 @@ } ], "minimum-stability": "dev", - "require": {} + "repositories": { + "git.egplusww.jp.Composer": { + "type": "composer", + "url": "https://git.egplusww.jp/api/packages/Composer/composer" + } + }, + "require-dev": { + "egrajp/corelibs-composer-all": "^9.13" + } } diff --git a/src/Autoloader.php b/src/Autoloader.php deleted file mode 100644 index da7e32a..0000000 --- a/src/Autoloader.php +++ /dev/null @@ -1,111 +0,0 @@ - 'Smarty.class.php'); - - /** - * Registers Smarty_Autoloader backward compatible to older installations. - * - * @param bool $prepend Whether to prepend the autoloader or not. - */ - public static function registerBC($prepend = false) - { - /** - * register the class autoloader - */ - if (!defined('SMARTY_SPL_AUTOLOAD')) { - define('SMARTY_SPL_AUTOLOAD', 0); - } - if (SMARTY_SPL_AUTOLOAD - && set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false - ) { - $registeredAutoLoadFunctions = spl_autoload_functions(); - if (!isset($registeredAutoLoadFunctions[ 'spl_autoload' ])) { - spl_autoload_register(); - } - } else { - self::register($prepend); - } - } - - /** - * Registers Smarty_Autoloader as an SPL autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not. - */ - public static function register($prepend = false) - { - self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : __DIR__ . DIRECTORY_SEPARATOR; - self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR : - self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR; - spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend); - } - - /** - * Handles auto loading of classes. - * - * @param string $class A class name. - */ - public static function autoload($class) - { - if ($class[ 0 ] !== 'S' || strpos($class, 'Smarty') !== 0) { - return; - } - $_class = smarty_strtolower_ascii($class); - if (isset(self::$rootClasses[ $_class ])) { - $file = self::$SMARTY_DIR . self::$rootClasses[ $_class ]; - if (is_file($file)) { - include $file; - } - } else { - $file = self::$SMARTY_SYSPLUGINS_DIR . $_class . '.php'; - if (is_file($file)) { - include $file; - } - } - return; - } -} diff --git a/src/BlockHandler/Base.php b/src/BlockHandler/Base.php new file mode 100644 index 0000000..e194f67 --- /dev/null +++ b/src/BlockHandler/Base.php @@ -0,0 +1,19 @@ +cacheable; + } +} \ No newline at end of file diff --git a/src/BlockHandler/BlockHandlerInterface.php b/src/BlockHandler/BlockHandlerInterface.php new file mode 100644 index 0000000..9aa744e --- /dev/null +++ b/src/BlockHandler/BlockHandlerInterface.php @@ -0,0 +1,10 @@ +callback = $callback; + $this->cacheable = $cacheable; + } + + public function handle($params, $content, Template $template, &$repeat) { + return call_user_func_array($this->callback, [$params, $content, &$template, &$repeat]); + } +} \ No newline at end of file diff --git a/src/BlockHandler/T.php b/src/BlockHandler/T.php new file mode 100644 index 0000000..a033bba --- /dev/null +++ b/src/BlockHandler/T.php @@ -0,0 +1,225 @@ + + * @throws \Smarty\Exception + */ +class T implements BlockHandlerInterface +{ + /** + * Replaces arguments in a string with their values. + * Arguments are represented by % followed by their number. + * + * @param string $str Source string + * @param mixed mixed Arguments, can be passed in an array or through single variables. + * @return string Modified string + */ + private function smartyGettextStrArg($str/*, $varargs... */) + { + $tr = []; + $p = 0; + + $nargs = func_num_args(); + for ($i = 1; $i < $nargs; $i++) { + $arg = func_get_arg($i); + + if (is_array($arg)) { + foreach ($arg as $aarg) { + $tr['%' . ++$p] = $aarg; + } + } else { + $tr['%' . ++$p] = $arg; + } + } + + return strtr($str, $tr); + } + + /** + * main handler + * + * @param array $params + * @param string $content + * @param Template $template + * @param boolean $repeat + * @return void + */ + public function handle($params, $content, Template $template, &$repeat) + { + if (is_null($content)) { + return; + } + + $escape = 'html'; + $plural = null; + $count = null; + $domain = null; + $context = null; + + foreach ($params as $_key => $_value) { + switch ($_key) { + // set escape mode, default html escape + case 'escape': + $escape = (string)$_value; + break; + // set plural parameters 'plural' and 'count'. + case 'plural': + $plural = $_value; + break; + // set count, only for plural, else ignored + case 'count': + $count = $_value; + break; + // get domain param + case 'domain': + $domain = (string)$_value; + break; + // get context param + case 'context': + $context = (string)$_value; + break; + } + } + + // use plural if required parameters are set + if (isset($count) && isset($plural)) { + if (isset($domain) && isset($context)) { + if (is_callable('_dnpgettext')) { + $content = _dnpgettext($domain, $context, $content, $plural, $count); + }/* elseif (is_callable('dnpgettext')) { + $content = dnpgettext($domain, $context, $content, $plural, $count); + } */ + } elseif (isset($domain)) { + if (is_callable('_dngettext')) { + $content = _dngettext($domain, $content, $plural, $count); + } elseif (is_callable('dngettext')) { + $content = dngettext($domain, $content, $plural, $count); + } + } elseif (isset($context)) { + if (is_callable('_npgettext')) { + $content = _npgettext($context, $content, $plural, $count); + }/* elseif (is_callable('npgettext')) { + $content = npgettext($context, $content, $plural, $count); + } */ + } else { + if (is_callable('_ngettext')) { + $content = _ngettext($content, $plural, $count); + } elseif (is_callable('ngettext')) { + $content = ngettext($content, $plural, $count); + } + } + } else { // use normal + if (isset($domain) && isset($context)) { + if (is_callable('_dpgettext')) { + $content = _dpgettext($domain, $context, $content); + }/* elseif (is_callable('dpgettext')) { + $content = dpgettext($domain, $context, $content); + } */ + } elseif (isset($domain)) { + if (is_callable('_dgettext')) { + $content = _dgettext($domain, $content); + } elseif (is_callable('dpgettext')) { + $content = dgettext($domain, $content); + } + } elseif (isset($context)) { + if (is_callable('_pgettext')) { + $content = _pgettext($context, $content); + }/* elseif (is_callable('pgettext')) { + $content = pgettext($context, $content); + } */ + } else { + if (is_callable('_gettext')) { + $content = _gettext($content); + } elseif (is_callable('gettext')) { + $content = gettext($content); + } + } + } + + // run strarg if there are parameters + if (count($params)) { + $content = $this->smartyGettextStrArg($content, $params); + } + + switch ($escape) { + case 'html': + // default + $content = nl2br(htmlspecialchars($content)); + break; + case 'javascript': + case 'js': + // javascript escape + $content = strtr( + $content, + [ + '\\' => '\\\\', + "'" => "\\'", + '"' => '\\"', + "\r" => '\\r', + "\n" => '\\n', + ' '<\/' + ] + ); + break; + case 'url': + // url escape + $content = urlencode($content); + break; + // below is a list for explicit OFF + case 'no': + case 'off': + case 'false': + case '0': + case 0: + // explicit OFF + default: + break; + } + + return $content; + } + + public function isCacheable(): bool + { + return true; + } +} + +// __END__ diff --git a/src/BlockHandler/TextFormat.php b/src/BlockHandler/TextFormat.php new file mode 100644 index 0000000..b4fa5ac --- /dev/null +++ b/src/BlockHandler/TextFormat.php @@ -0,0 +1,110 @@ + + * @throws \Smarty\Exception + */ +class TextFormat implements BlockHandlerInterface { + + public function handle($params, $content, Template $template, &$repeat) { + if (is_null($content)) { + return; + } + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string)$_val; + break; + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int)$_val; + break; + case 'wrap_cut': + $$_key = (bool)$_val; + break; + default: + trigger_error("textformat: unknown attribute '{$_key}'"); + } + } + if ($style === 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n]{2}!', $content); + foreach ($_paragraphs as &$_paragraph) { + if (!$_paragraph) { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraph = + preg_replace( + array( + '!\s+!' . Smarty::$_UTF8_MODIFIER, + '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER + ), + array( + ' ', + '' + ), + $_paragraph + ); + // indent first line + if ($indent_first > 0) { + $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; + } + // wordwrap sentences + $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + // indent lines + if ($indent > 0) { + $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + if ($assign) { + $template->assign($assign, $_output); + } else { + return $_output; + } + } + + public function isCacheable(): bool { + return true; + } +} \ No newline at end of file diff --git a/src/Cacheresource/Base.php b/src/Cacheresource/Base.php new file mode 100644 index 0000000..54a1419 --- /dev/null +++ b/src/Cacheresource/Base.php @@ -0,0 +1,156 @@ +hasLock($smarty, $cached)) { + $hadLock = true; + if (microtime(true) - $start > $smarty->locking_timeout) { + // abort waiting for lock release + return false; + } + sleep(1); + } + return $hadLock; + } + + /** + * Check is cache is locked for this template + * + * @param Smarty $smarty + * @param Cached $cached + * + * @return bool + */ + public function hasLock(Smarty $smarty, Cached $cached) + { + // check if lock exists + return false; + } + + /** + * Lock cache for this template + * + * @param Smarty $smarty + * @param Cached $cached + * + * @return bool + */ + public function acquireLock(Smarty $smarty, Cached $cached) + { + // create lock + return true; + } + + /** + * Unlock cache for this template + * + * @param Smarty $smarty + * @param Cached $cached + * + * @return bool + */ + public function releaseLock(Smarty $smarty, Cached $cached) + { + // release lock + return true; + } +} diff --git a/src/sysplugins/smarty_cacheresource_custom.php b/src/Cacheresource/Custom.php similarity index 64% rename from src/sysplugins/smarty_cacheresource_custom.php rename to src/Cacheresource/Custom.php index 68ad112..c049246 100644 --- a/src/sysplugins/smarty_cacheresource_custom.php +++ b/src/Cacheresource/Custom.php @@ -1,19 +1,26 @@ cache_id) ? preg_replace('![^\w\|]+!', '_', $cached->cache_id) : null; $_compile_id = isset($cached->compile_id) ? preg_replace('![^\w]+!', '_', $cached->compile_id) : null; - $path = $cached->source->uid . $_cache_id . $_compile_id; + $path = $cached->getSource()->uid . $_cache_id . $_compile_id; $cached->filepath = sha1($path); - if ($_template->smarty->cache_locking) { + if ($_template->getSmarty()->cache_locking) { $cached->lock_id = sha1('lock.' . $path); } $this->populateTimestamp($cached); @@ -95,14 +102,14 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource /** * populate Cached Object with timestamp and exists from Resource * - * @param Smarty_Template_Cached $cached + * @param \Smarty\Template\Cached $cached * * @return void */ - public function populateTimestamp(Smarty_Template_Cached $cached) + public function populateTimestamp(\Smarty\Template\Cached $cached) { $mtime = - $this->fetchTimestamp($cached->filepath, $cached->source->name, $cached->cache_id, $cached->compile_id); + $this->fetchTimestamp($cached->filepath, $cached->getSource()->name, $cached->cache_id, $cached->compile_id); if ($mtime !== null) { $cached->timestamp = $mtime; $cached->exists = !!$cached->timestamp; @@ -111,7 +118,7 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource $timestamp = null; $this->fetch( $cached->filepath, - $cached->source->name, + $cached->getSource()->name, $cached->cache_id, $cached->compile_id, $cached->content, @@ -121,29 +128,29 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource $cached->exists = !!$cached->timestamp; } - /** - * Read the cached template and process the header - * - * @param \Smarty_Internal_Template $_smarty_tpl do not change variable name, is used by compiled template - * @param Smarty_Template_Cached $cached cached object - * @param boolean $update flag if called because cache update - * - * @return boolean true or false if the cached content does not exist - */ + /** + * Read the cached template and process the header + * + * @param Template $_smarty_tpl do not change variable name, is used by compiled template + * @param Cached|null $cached cached object + * @param boolean $update flag if called because cache update + * + * @return boolean true or false if the cached content does not exist + */ public function process( - Smarty_Internal_Template $_smarty_tpl, - Smarty_Template_Cached $cached = null, - $update = false + Template $_smarty_tpl, + \Smarty\Template\Cached $cached = null, + $update = false ) { if (!$cached) { - $cached = $_smarty_tpl->cached; + $cached = $_smarty_tpl->getCached(); } $content = $cached->content ? $cached->content : null; $timestamp = $cached->timestamp ? $cached->timestamp : null; if ($content === null || !$timestamp) { $this->fetch( - $_smarty_tpl->cached->filepath, - $_smarty_tpl->source->name, + $_smarty_tpl->getCached()->filepath, + $_smarty_tpl->getSource()->name, $_smarty_tpl->cache_id, $_smarty_tpl->compile_id, $content, @@ -161,16 +168,16 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource /** * Write the rendered template output to cache * - * @param Smarty_Internal_Template $_template template object + * @param Template $_template template object * @param string $content content to cache * * @return boolean success */ - public function writeCachedContent(Smarty_Internal_Template $_template, $content) + public function storeCachedContent(Template $_template, $content) { return $this->save( - $_template->cached->filepath, - $_template->source->name, + $_template->getCached()->filepath, + $_template->getSource()->name, $_template->cache_id, $_template->compile_id, $_template->cache_lifetime, @@ -181,19 +188,18 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource /** * Read cached template from cache * - * @param Smarty_Internal_Template $_template template object + * @param Template $_template template object * * @return string|boolean content */ - public function readCachedContent(Smarty_Internal_Template $_template) + public function retrieveCachedContent(Template $_template) { - $content = $_template->cached->content ? $_template->cached->content : null; - $timestamp = null; - if ($content === null) { + $content = $_template->getCached()->content ?: null; + if ($content === null) { $timestamp = null; $this->fetch( - $_template->cached->filepath, - $_template->source->name, + $_template->getCached()->filepath, + $_template->getSource()->name, $_template->cache_id, $_template->compile_id, $content, @@ -206,15 +212,15 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource return false; } - /** - * Empty cache - * - * @param Smarty $smarty Smarty object - * @param integer $exp_time expiration time (number of seconds, not timestamp) - * - * @return integer number of cache files deleted - */ - public function clearAll(Smarty $smarty, $exp_time = null) + /** + * Empty cache + * + * @param \Smarty\Smarty $smarty Smarty object + * @param null $exp_time expiration time (number of seconds, not timestamp) + * + * @return integer number of cache files deleted + */ + public function clearAll(\Smarty\Smarty $smarty, $exp_time = null) { return $this->delete(null, null, null, $exp_time); } @@ -222,20 +228,20 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource /** * Empty cache for a specific template * - * @param Smarty $smarty Smarty object + * @param \Smarty\Smarty $smarty Smarty object * @param string $resource_name template name * @param string $cache_id cache id * @param string $compile_id compile id * @param integer $exp_time expiration time (number of seconds, not timestamp) * * @return int number of cache files deleted - * @throws \SmartyException + * @throws \Smarty\Exception */ - public function clear(Smarty $smarty, $resource_name, $cache_id, $compile_id, $exp_time) + public function clear(\Smarty\Smarty $smarty, $resource_name, $cache_id, $compile_id, $exp_time) { $cache_name = null; if (isset($resource_name)) { - $source = Smarty_Template_Source::load(null, $smarty, $resource_name); + $source = \Smarty\Template\Source::load(null, $smarty, $resource_name); if ($source->exists) { $cache_name = $source->name; } else { @@ -245,18 +251,18 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource return $this->delete($cache_name, $cache_id, $compile_id, $exp_time); } - /** - * Check is cache is locked for this template - * - * @param Smarty $smarty Smarty object - * @param Smarty_Template_Cached $cached cached object - * - * @return boolean true or false if cache is locked - */ - public function hasLock(Smarty $smarty, Smarty_Template_Cached $cached) + /** + * Check is cache is locked for this template + * + * @param Smarty $smarty Smarty object + * @param Cached $cached cached object + * + * @return boolean true or false if cache is locked + */ + public function hasLock(\Smarty\Smarty $smarty, \Smarty\Template\Cached $cached) { $id = $cached->lock_id; - $name = $cached->source->name . '.lock'; + $name = $cached->getSource()->name . '.lock'; $mtime = $this->fetchTimestamp($id, $name, $cached->cache_id, $cached->compile_id); if ($mtime === null) { $this->fetch($id, $name, $cached->cache_id, $cached->compile_id, $content, $mtime); @@ -267,31 +273,31 @@ abstract class Smarty_CacheResource_Custom extends Smarty_CacheResource /** * Lock cache for this template * - * @param Smarty $smarty Smarty object - * @param Smarty_Template_Cached $cached cached object + * @param \Smarty\Smarty $smarty Smarty object + * @param \Smarty\Template\Cached $cached cached object * * @return bool|void */ - public function acquireLock(Smarty $smarty, Smarty_Template_Cached $cached) + public function acquireLock(\Smarty\Smarty $smarty, \Smarty\Template\Cached $cached) { $cached->is_locked = true; $id = $cached->lock_id; - $name = $cached->source->name . '.lock'; + $name = $cached->getSource()->name . '.lock'; $this->save($id, $name, $cached->cache_id, $cached->compile_id, $smarty->locking_timeout, ''); } /** * Unlock cache for this template * - * @param Smarty $smarty Smarty object - * @param Smarty_Template_Cached $cached cached object + * @param \Smarty\Smarty $smarty Smarty object + * @param \Smarty\Template\Cached $cached cached object * * @return bool|void */ - public function releaseLock(Smarty $smarty, Smarty_Template_Cached $cached) + public function releaseLock(\Smarty\Smarty $smarty, \Smarty\Template\Cached $cached) { $cached->is_locked = false; - $name = $cached->source->name . '.lock'; + $name = $cached->getSource()->name . '.lock'; $this->delete($name, $cached->cache_id, $cached->compile_id, null); } } diff --git a/src/Cacheresource/File.php b/src/Cacheresource/File.php new file mode 100644 index 0000000..538f010 --- /dev/null +++ b/src/Cacheresource/File.php @@ -0,0 +1,338 @@ +getSource(); + $smarty = $_template->getSmarty(); + $_compile_dir_sep = $smarty->use_sub_dirs ? DIRECTORY_SEPARATOR : '^'; + $_filepath = $source->uid; + $cached->filepath = $smarty->getCacheDir(); + if (isset($_template->cache_id)) { + $cached->filepath .= preg_replace( + array( + '![^\w|]+!', + '![|]+!' + ), + array( + '_', + $_compile_dir_sep + ), + $_template->cache_id + ) . $_compile_dir_sep; + } + if (isset($_template->compile_id)) { + $cached->filepath .= preg_replace('![^\w]+!', '_', $_template->compile_id) . $_compile_dir_sep; + } + // if use_sub_dirs, break file into directories + if ($smarty->use_sub_dirs) { + $cached->filepath .= $_filepath[ 0 ] . $_filepath[ 1 ] . DIRECTORY_SEPARATOR . $_filepath[ 2 ] . + $_filepath[ 3 ] . + DIRECTORY_SEPARATOR . + $_filepath[ 4 ] . $_filepath[ 5 ] . DIRECTORY_SEPARATOR; + } + $cached->filepath .= $_filepath . '_' . $source->getBasename(); + + if ($smarty->cache_locking) { + $cached->lock_id = $cached->filepath . '.lock'; + } + $cached->filepath .= '.php'; + $cached->timestamp = $cached->exists = is_file($cached->filepath); + if ($cached->exists) { + $cached->timestamp = filemtime($cached->filepath); + } + } + + /** + * populate Cached Object with timestamp and exists from Resource + * + * @param Cached $cached cached object + * + * @return void + */ + public function populateTimestamp(Cached $cached) + { + $cached->timestamp = $cached->exists = is_file($cached->filepath); + if ($cached->exists) { + $cached->timestamp = filemtime($cached->filepath); + } + } + + /** + * Read the cached template and process its header + * + * @param Template $_smarty_tpl do not change variable name, is used by compiled template + * @param Cached|null $cached cached object + * @param bool $update flag if called because cache update + * + * @return boolean true or false if the cached content does not exist + */ + public function process( + Template $_smarty_tpl, + Cached $cached = null, + $update = false + ) { + $_smarty_tpl->getCached()->setValid(false); + if ($update && defined('HHVM_VERSION')) { + eval('?>' . file_get_contents($_smarty_tpl->getCached()->filepath)); + return true; + } else { + return @include $_smarty_tpl->getCached()->filepath; + } + } + + /** + * Write the rendered template output to cache + * + * @param Template $_template template object + * @param string $content content to cache + * + * @return bool success + * @throws \Smarty\Exception + */ + public function storeCachedContent(Template $_template, $content) + { + if ($_template->getSmarty()->writeFile($_template->getCached()->filepath, $content) === true) { + if (function_exists('opcache_invalidate') + && (!function_exists('ini_get') || strlen(ini_get('opcache.restrict_api'))) < 1 + ) { + opcache_invalidate($_template->getCached()->filepath, true); + } elseif (function_exists('apc_compile_file')) { + apc_compile_file($_template->getCached()->filepath); + } + $cached = $_template->getCached(); + $cached->timestamp = $cached->exists = is_file($cached->filepath); + if ($cached->exists) { + $cached->timestamp = filemtime($cached->filepath); + return true; + } + } + return false; + } + + /** + * Read cached template from cache + * + * @param Template $_template template object + * + * @return string content + */ + public function retrieveCachedContent(Template $_template) + { + if (is_file($_template->getCached()->filepath)) { + return file_get_contents($_template->getCached()->filepath); + } + return false; + } + + /** + * Empty cache + * + * @param Smarty $smarty + * @param integer $exp_time expiration time (number of seconds, not timestamp) + * + * @return integer number of cache files deleted + */ + public function clearAll(Smarty $smarty, $exp_time = null) + { + return $this->clear($smarty, null, null, null, $exp_time); + } + + /** + * Empty cache for a specific template + * + * @param Smarty $smarty + * @param string $resource_name template name + * @param string $cache_id cache id + * @param string $compile_id compile id + * @param integer $exp_time expiration time (number of seconds, not timestamp) + * + * @return integer number of cache files deleted + */ + public function clear(Smarty $smarty, $resource_name, $cache_id, $compile_id, $exp_time) + { + $_cache_id = isset($cache_id) ? preg_replace('![^\w\|]+!', '_', $cache_id) : null; + $_compile_id = isset($compile_id) ? preg_replace('![^\w]+!', '_', $compile_id) : null; + $_dir_sep = $smarty->use_sub_dirs ? '/' : '^'; + $_compile_id_offset = $smarty->use_sub_dirs ? 3 : 0; + $_dir = $smarty->getCacheDir(); + if ($_dir === '/') { //We should never want to delete this! + return 0; + } + $_dir_length = strlen($_dir); + if (isset($_cache_id)) { + $_cache_id_parts = explode('|', $_cache_id); + $_cache_id_parts_count = count($_cache_id_parts); + if ($smarty->use_sub_dirs) { + foreach ($_cache_id_parts as $id_part) { + $_dir .= $id_part . '/'; + } + } + } + if (isset($resource_name)) { + $_save_stat = $smarty->caching; + $smarty->caching = \Smarty\Smarty::CACHING_LIFETIME_CURRENT; + $tpl = $smarty->doCreateTemplate($resource_name); + $smarty->caching = $_save_stat; + // remove from template cache + if ($tpl->getSource()->exists) { + $_resourcename_parts = basename(str_replace('^', '/', $tpl->getCached()->filepath)); + } else { + return 0; + } + } + $_count = 0; + $_time = time(); + if (file_exists($_dir)) { + $_cacheDirs = new RecursiveDirectoryIterator($_dir); + $_cache = new RecursiveIteratorIterator($_cacheDirs, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($_cache as $_file) { + if (substr(basename($_file->getPathname()), 0, 1) === '.') { + continue; + } + $_filepath = (string)$_file; + // directory ? + if ($_file->isDir()) { + if (!$_cache->isDot()) { + // delete folder if empty + @rmdir($_file->getPathname()); + } + } else { + // delete only php files + if (substr($_filepath, -4) !== '.php') { + continue; + } + $_parts = explode($_dir_sep, str_replace('\\', '/', substr($_filepath, $_dir_length))); + $_parts_count = count($_parts); + // check name + if (isset($resource_name)) { + if ($_parts[ $_parts_count - 1 ] !== $_resourcename_parts) { + continue; + } + } + // check compile id + if (isset($_compile_id) && (!isset($_parts[ $_parts_count - 2 - $_compile_id_offset ]) + || $_parts[ $_parts_count - 2 - $_compile_id_offset ] !== $_compile_id) + ) { + continue; + } + // check cache id + if (isset($_cache_id)) { + // count of cache id parts + $_parts_count = (isset($_compile_id)) ? $_parts_count - 2 - $_compile_id_offset : + $_parts_count - 1 - $_compile_id_offset; + if ($_parts_count < $_cache_id_parts_count) { + continue; + } + for ($i = 0; $i < $_cache_id_parts_count; $i++) { + if ($_parts[ $i ] !== $_cache_id_parts[ $i ]) { + continue 2; + } + } + } + if (is_file($_filepath)) { + // expired ? + if (isset($exp_time)) { + if ($exp_time < 0) { + preg_match('#\'cache_lifetime\' =>\s*(\d*)#', file_get_contents($_filepath), $match); + if ($_time < (filemtime($_filepath) + $match[ 1 ])) { + continue; + } + } else { + if ($_time - filemtime($_filepath) < $exp_time) { + continue; + } + } + } + $_count += @unlink($_filepath) ? 1 : 0; + if (function_exists('opcache_invalidate') + && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1) + ) { + opcache_invalidate($_filepath, true); + } elseif (function_exists('apc_delete_file')) { + apc_delete_file($_filepath); + } + } + } + } + } + return $_count; + } + + /** + * Check is cache is locked for this template + * + * @param Smarty $smarty Smarty object + * @param Cached $cached cached object + * + * @return boolean true or false if cache is locked + */ + public function hasLock(Smarty $smarty, Cached $cached) + { + clearstatcache(true, $cached->lock_id ?? ''); + if (null !== $cached->lock_id && is_file($cached->lock_id)) { + $t = filemtime($cached->lock_id); + return $t && (time() - $t < $smarty->locking_timeout); + } else { + return false; + } + } + + /** + * Lock cache for this template + * + * @param Smarty $smarty Smarty object + * @param Cached $cached cached object + * + * @return void + */ + public function acquireLock(Smarty $smarty, Cached $cached) + { + $cached->is_locked = true; + touch($cached->lock_id); + } + + /** + * Unlock cache for this template + * + * @param Smarty $smarty Smarty object + * @param Cached $cached cached object + * + * @return void + */ + public function releaseLock(Smarty $smarty, Cached $cached) + { + $cached->is_locked = false; + @unlink($cached->lock_id); + } +} diff --git a/src/sysplugins/smarty_cacheresource_keyvaluestore.php b/src/Cacheresource/KeyValueStore.php similarity index 83% rename from src/sysplugins/smarty_cacheresource_keyvaluestore.php rename to src/Cacheresource/KeyValueStore.php index 4b1c0f6..733d277 100644 --- a/src/sysplugins/smarty_cacheresource_keyvaluestore.php +++ b/src/Cacheresource/KeyValueStore.php @@ -1,9 +1,16 @@ filepath = $_template->source->uid . '#' . $this->sanitize($cached->source->resource) . '#' . + $cached->filepath = $_template->getSource()->uid . '#' . $this->sanitize($cached->getSource()->resource) . '#' . $this->sanitize($cached->cache_id) . '#' . $this->sanitize($cached->compile_id); $this->populateTimestamp($cached); } @@ -62,20 +69,20 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource /** * populate Cached Object with timestamp and exists from Resource * - * @param Smarty_Template_Cached $cached cached object + * @param Cached $cached cached object * * @return void */ - public function populateTimestamp(Smarty_Template_Cached $cached) + public function populateTimestamp(Cached $cached) { if (!$this->fetch( $cached->filepath, - $cached->source->name, + $cached->getSource()->name, $cached->cache_id, $cached->compile_id, $content, $timestamp, - $cached->source->uid + $cached->getSource()->uid ) ) { return; @@ -85,34 +92,34 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource $cached->exists = !!$cached->timestamp; } - /** - * Read the cached template and process the header - * - * @param \Smarty_Internal_Template $_smarty_tpl do not change variable name, is used by compiled template - * @param Smarty_Template_Cached $cached cached object - * @param boolean $update flag if called because cache update - * - * @return boolean true or false if the cached content does not exist - */ + /** + * Read the cached template and process the header + * + * @param Template $_smarty_tpl do not change variable name, is used by compiled template + * @param Cached|null $cached cached object + * @param boolean $update flag if called because cache update + * + * @return boolean true or false if the cached content does not exist + */ public function process( - Smarty_Internal_Template $_smarty_tpl, - Smarty_Template_Cached $cached = null, - $update = false + Template $_smarty_tpl, + Cached $cached = null, + $update = false ) { if (!$cached) { - $cached = $_smarty_tpl->cached; + $cached = $_smarty_tpl->getCached(); } - $content = $cached->content ? $cached->content : null; - $timestamp = $cached->timestamp ? $cached->timestamp : null; + $content = $cached->content ?: null; + $timestamp = $cached->timestamp ?: null; if ($content === null || !$timestamp) { if (!$this->fetch( - $_smarty_tpl->cached->filepath, - $_smarty_tpl->source->name, + $_smarty_tpl->getCached()->filepath, + $_smarty_tpl->getSource()->name, $_smarty_tpl->cache_id, $_smarty_tpl->compile_id, $content, $timestamp, - $_smarty_tpl->source->uid + $_smarty_tpl->getSource()->uid ) ) { return false; @@ -128,37 +135,37 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource /** * Write the rendered template output to cache * - * @param Smarty_Internal_Template $_template template object + * @param Template $_template template object * @param string $content content to cache * * @return boolean success */ - public function writeCachedContent(Smarty_Internal_Template $_template, $content) + public function storeCachedContent(Template $_template, $content) { $this->addMetaTimestamp($content); - return $this->write(array($_template->cached->filepath => $content), $_template->cache_lifetime); + return $this->write(array($_template->getCached()->filepath => $content), $_template->cache_lifetime); } /** * Read cached template from cache * - * @param Smarty_Internal_Template $_template template object + * @param Template $_template template object * * @return string|false content */ - public function readCachedContent(Smarty_Internal_Template $_template) + public function retrieveCachedContent(Template $_template) { - $content = $_template->cached->content ? $_template->cached->content : null; + $content = $_template->getCached()->content ?: null; $timestamp = null; if ($content === null) { if (!$this->fetch( - $_template->cached->filepath, - $_template->source->name, + $_template->getCached()->filepath, + $_template->getSource()->name, $_template->cache_id, $_template->compile_id, $content, $timestamp, - $_template->source->uid + $_template->getSource()->uid ) ) { return false; @@ -200,7 +207,7 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource * @param integer $exp_time expiration time [being ignored] * * @return int number of cache files deleted [always -1] - * @throws \SmartyException + * @throws \Smarty\Exception * @uses buildCachedFilepath() to generate the CacheID * @uses invalidate() to mark CacheIDs parent chain as outdated * @uses delete() to remove CacheID from cache @@ -222,12 +229,12 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource * @param string $resource_name template name * * @return string filepath of cache file - * @throws \SmartyException + * @throws \Smarty\Exception */ protected function getTemplateUid(Smarty $smarty, $resource_name) { if (isset($resource_name)) { - $source = Smarty_Template_Source::load(null, $smarty, $resource_name); + $source = \Smarty\Template\Source::load(null, $smarty, $resource_name); if ($source->exists) { return $source->uid; } @@ -380,11 +387,7 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource $compile_id = null, $resource_uid = null ) { - // abort if there is no CacheID - if (false && !$cid) { - return 0; - } - // abort if there are no InvalidationKeys to check + // abort if there are no InvalidationKeys to check if (!($_cid = $this->listInvalidationKeys($cid, $resource_name, $cache_id, $compile_id, $resource_uid))) { return 0; } @@ -457,11 +460,11 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource * Check is cache is locked for this template * * @param Smarty $smarty Smarty object - * @param Smarty_Template_Cached $cached cached object + * @param Cached $cached cached object * * @return boolean true or false if cache is locked */ - public function hasLock(Smarty $smarty, Smarty_Template_Cached $cached) + public function hasLock(Smarty $smarty, Cached $cached) { $key = 'LOCK#' . $cached->filepath; $data = $this->read(array($key)); @@ -472,11 +475,11 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource * Lock cache for this template * * @param Smarty $smarty Smarty object - * @param Smarty_Template_Cached $cached cached object + * @param Cached $cached cached object * * @return bool|void */ - public function acquireLock(Smarty $smarty, Smarty_Template_Cached $cached) + public function acquireLock(Smarty $smarty, Cached $cached) { $cached->is_locked = true; $key = 'LOCK#' . $cached->filepath; @@ -487,11 +490,11 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource * Unlock cache for this template * * @param Smarty $smarty Smarty object - * @param Smarty_Template_Cached $cached cached object + * @param Cached $cached cached object * - * @return bool|void + * @return void */ - public function releaseLock(Smarty $smarty, Smarty_Template_Cached $cached) + public function releaseLock(Smarty $smarty, Cached $cached) { $cached->is_locked = false; $key = 'LOCK#' . $cached->filepath; diff --git a/src/Compile/Base.php b/src/Compile/Base.php new file mode 100644 index 0000000..2d5c0c0 --- /dev/null +++ b/src/Compile/Base.php @@ -0,0 +1,233 @@ +cacheable; + } + + /** + * Converts attributes into parameter array strings + * + * @param array $_attr + * + * @return array + */ + protected function formatParamsArray(array $_attr): array { + $_paramsArray = []; + foreach ($_attr as $_key => $_value) { + $_paramsArray[] = var_export($_key, true) . "=>" . $_value; + } + return $_paramsArray; + } + + /** + * This function checks if the attributes passed are valid + * The attributes passed for the tag to compile are checked against the list of required and + * optional attributes. Required attributes must be present. Optional attributes are check against + * the corresponding list. The keyword '_any' specifies that any attribute will be accepted + * as valid + * + * @param object $compiler compiler object + * @param array $attributes attributes applied to the tag + * + * @return array of mapped attributes for further processing + */ + protected function getAttributes($compiler, $attributes) { + $_indexed_attr = []; + $options = array_fill_keys($this->option_flags, true); + foreach ($attributes as $key => $mixed) { + // shorthand ? + if (!is_array($mixed)) { + // options flag ? + if (isset($options[trim($mixed, '\'"')])) { + $_indexed_attr[trim($mixed, '\'"')] = true; + // shorthand attribute ? + } elseif (isset($this->shorttag_order[$key])) { + $_indexed_attr[$this->shorttag_order[$key]] = $mixed; + } else { + // too many shorthands + $compiler->trigger_template_error('too many shorthand attributes', null, true); + } + // named attribute + } else { + foreach ($mixed as $k => $v) { + // options flag? + if (isset($options[$k])) { + if (is_bool($v)) { + $_indexed_attr[$k] = $v; + } else { + if (is_string($v)) { + $v = trim($v, '\'" '); + } + + // Mapping array for boolean option value + static $optionMap = [1 => true, 0 => false, 'true' => true, 'false' => false]; + + if (isset($optionMap[$v])) { + $_indexed_attr[$k] = $optionMap[$v]; + } else { + $compiler->trigger_template_error( + "illegal value '" . var_export($v, true) . + "' for options flag '{$k}'", + null, + true + ); + } + } + // must be named attribute + } else { + $_indexed_attr[$k] = $v; + } + } + } + } + // check if all required attributes present + foreach ($this->required_attributes as $attr) { + if (!isset($_indexed_attr[$attr])) { + $compiler->trigger_template_error("missing '{$attr}' attribute", null, true); + } + } + // check for not allowed attributes + if ($this->optional_attributes !== ['_any']) { + $allowedAttributes = array_fill_keys( + array_merge( + $this->required_attributes, + $this->optional_attributes, + $this->option_flags + ), + true + ); + foreach ($_indexed_attr as $key => $dummy) { + if (!isset($allowedAttributes[$key]) && $key !== 0) { + $compiler->trigger_template_error("unexpected '{$key}' attribute", null, true); + } + } + } + // default 'false' for all options flags not set + foreach ($this->option_flags as $flag) { + if (!isset($_indexed_attr[$flag])) { + $_indexed_attr[$flag] = false; + } + } + + return $_indexed_attr; + } + + /** + * Push opening tag name on stack + * Optionally additional data can be saved on stack + * + * @param Template $compiler compiler object + * @param string $openTag the opening tag's name + * @param mixed $data optional data saved + */ + protected function openTag(Template $compiler, $openTag, $data = null) { + $compiler->openTag($openTag, $data); + } + + /** + * Pop closing tag + * Raise an error if this stack-top doesn't match with expected opening tags + * + * @param Template $compiler compiler object + * @param array|string $expectedTag the expected opening tag names + * + * @return mixed any type the opening tag's name or saved data + */ + protected function closeTag(Template $compiler, $expectedTag) { + return $compiler->closeTag($expectedTag); + } + + /** + * @param mixed $scope + * @param array $invalidScopes + * + * @return int + * @throws Exception + */ + protected function convertScope($scope): int { + + static $scopes = [ + 'local' => Data::SCOPE_LOCAL, // current scope + 'parent' => Data::SCOPE_PARENT, // parent scope (definition unclear) + 'tpl_root' => Data::SCOPE_TPL_ROOT, // highest template (keep going up until parent is not a template) + 'root' => Data::SCOPE_ROOT, // highest scope (definition unclear) + 'global' => Data::SCOPE_GLOBAL, // smarty object + + 'smarty' => Data::SCOPE_SMARTY, // @deprecated alias of 'global' + ]; + + $_scopeName = trim($scope, '\'"'); + if (is_numeric($_scopeName) && in_array($_scopeName, $scopes)) { + return (int) $_scopeName; + } + + if (isset($scopes[$_scopeName])) { + return $scopes[$_scopeName]; + } + + $err = var_export($_scopeName, true); + throw new Exception("illegal value '{$err}' for \"scope\" attribute"); + } + + /** + * Compiles code for the tag + * + * @param array $args array with attributes from parser + * @param Template $compiler compiler object + * @param array $parameter array with compilation parameter + * + * @return string compiled code as a string + * @throws \Smarty\CompilerException + */ + abstract public function compile($args, Template $compiler, $parameter = array(), $tag = null, $function = null): string; +} diff --git a/src/Compile/BlockCompiler.php b/src/Compile/BlockCompiler.php new file mode 100644 index 0000000..e7a8f31 --- /dev/null +++ b/src/Compile/BlockCompiler.php @@ -0,0 +1,228 @@ +compileOpeningTag($compiler, $args, $tag, $function); + } else { + $output = $this->compileClosingTag($compiler, $tag, $parameter, $function); + } + return $output; + } + + /** + * Compiles code for the {$smarty.block.child} property + * + * @param Template $compiler compiler object + * + * @return string compiled code + * @throws CompilerException + */ + public function compileChild(\Smarty\Compiler\Template $compiler) { + + if (!isset($compiler->_cache['blockNesting'])) { + $compiler->trigger_template_error( + "'{\$smarty.block.child}' used outside {block} tags ", + $compiler->getParser()->lex->taglineno + ); + } + $compiler->_cache['blockParams'][$compiler->_cache['blockNesting']]['callsChild'] = true; + $compiler->suppressNocacheProcessing = true; + + $output = "getInheritance()->callChild($_smarty_tpl, $this' . ");\n"; + $output .= "?>\n"; + return $output; + } + + /** + * Compiles code for the {$smarty.block.parent} property + * + * @param Template $compiler compiler object + * + * @return string compiled code + * @throws CompilerException + */ + public function compileParent(\Smarty\Compiler\Template $compiler) { + + if (!isset($compiler->_cache['blockNesting'])) { + $compiler->trigger_template_error( + "'{\$smarty.block.parent}' used outside {block} tags ", + $compiler->getParser()->lex->taglineno + ); + } + $compiler->suppressNocacheProcessing = true; + + $output = "getInheritance()->callParent($_smarty_tpl, $this' . ");\n"; + $output .= "?>\n"; + return $output; + } + + /** + * Returns true if this block is cacheable. + * + * @param Smarty $smarty + * @param $function + * + * @return bool + */ + protected function blockIsCacheable(\Smarty\Smarty $smarty, $function): bool { + return $smarty->getBlockHandler($function)->isCacheable(); + } + + /** + * Returns the code used for the isset check + * + * @param string $tag tag name + * @param string $function base tag or method name + * + * @return string + */ + protected function getIsCallableCode($tag, $function): string { + return "\$_smarty_tpl->getSmarty()->getBlockHandler(" . var_export($function, true) . ")"; + } + + /** + * Returns the full code used to call the callback + * + * @param string $tag tag name + * @param string $function base tag or method name + * + * @return string + */ + protected function getFullCallbackCode($tag, $function): string { + return "\$_smarty_tpl->getSmarty()->getBlockHandler(" . var_export($function, true) . ")->handle"; + } + + /** + * @param Template $compiler + * @param array $args + * @param string|null $tag + * @param string|null $function + * + * @return string + */ + private function compileOpeningTag(Template $compiler, array $args, ?string $tag, ?string $function): string { + + // check and get attributes + $_attr = $this->getAttributes($compiler, $args); + $this->nesting++; + unset($_attr['nocache']); + $_params = 'array(' . implode(',', $this->formatParamsArray($_attr)) . ')'; + + if (!$this->blockIsCacheable($compiler->getSmarty(), $function)) { + $compiler->tag_nocache = true; + } + + if ($compiler->tag_nocache) { + // push a {nocache} tag onto the stack to prevent caching of this block + $this->openTag($compiler, 'nocache'); + } + + $this->openTag($compiler, $tag, [$_params, $compiler->tag_nocache]); + + // compile code + $output = "getIsCallableCode($tag, $function) .") {\nthrow new \\Smarty\\Exception('block tag \'{$tag}\' not callable or registered');\n}\n +echo " . $this->getFullCallbackCode($tag, $function) . "({$_params}, null, \$_smarty_tpl, \$_block_repeat); +while (\$_block_repeat) { + ob_start(); +?>"; + + return $output; + } + + /** + * @param Template $compiler + * @param string $tag + * @param array $parameter + * @param string|null $function + * + * @return string + * @throws CompilerException + * @throws Exception + */ + private function compileClosingTag(Template $compiler, string $tag, array $parameter, ?string $function): string { + + // closing tag of block plugin, restore nocache + $base_tag = substr($tag, 0, -5); + [$_params, $nocache_pushed] = $this->closeTag($compiler, $base_tag); + + // compile code + if (!isset($parameter['modifier_list'])) { + $mod_pre = $mod_post = $mod_content = ''; + $mod_content2 = 'ob_get_clean()'; + } else { + $mod_content2 = "\$_block_content{$this->nesting}"; + $mod_content = "\$_block_content{$this->nesting} = ob_get_clean();\n"; + $mod_pre = "ob_start();\n"; + $mod_post = 'echo ' . $compiler->compileModifier($parameter['modifier_list'], 'ob_get_clean()') + . ";\n"; + } + $output = "getFullCallbackCode($base_tag, $function); + $output .= "echo {$callback}({$_params}, {$mod_content2}, \$_smarty_tpl, \$_block_repeat);\n"; + $output .= "{$mod_post}}\n?>"; + + if ($nocache_pushed) { + // pop the pushed virtual nocache tag + $this->closeTag($compiler, 'nocache'); + $compiler->tag_nocache = true; + } + + return $output; + } + +} diff --git a/src/Compile/CompilerInterface.php b/src/Compile/CompilerInterface.php new file mode 100644 index 0000000..5f2cc7c --- /dev/null +++ b/src/Compile/CompilerInterface.php @@ -0,0 +1,26 @@ +getSmarty()->getRuntime('DefaultPluginHandler')->hasPlugin(" . + var_export($function, true) . ", 'block')"; + } + + /** + * @inheritDoc + */ + protected function getFullCallbackCode($tag, $function): string { + return "\$_smarty_tpl->getSmarty()->getRuntime('DefaultPluginHandler')->getCallback(" . + var_export($function, true) . ", 'block')"; + } + + /** + * @inheritDoc + */ + protected function blockIsCacheable(\Smarty\Smarty $smarty, $function): bool { + return true; + } + +} \ No newline at end of file diff --git a/src/Compile/DefaultHandlerFunctionCallCompiler.php b/src/Compile/DefaultHandlerFunctionCallCompiler.php new file mode 100644 index 0000000..e6d1138 --- /dev/null +++ b/src/Compile/DefaultHandlerFunctionCallCompiler.php @@ -0,0 +1,47 @@ +getAttributes($compiler, $args); + unset($_attr['nocache']); + + $_paramsArray = $this->formatParamsArray($_attr); + $_params = 'array(' . implode(',', $_paramsArray) . ')'; + + $output = "\$_smarty_tpl->getSmarty()->getRuntime('DefaultPluginHandler')->getCallback(" . var_export($function, true) . + ",'function')($_params, \$_smarty_tpl)"; + + if (!empty($parameter['modifierlist'])) { + $output = $compiler->compileModifier($parameter['modifierlist'], $output); + } + return "\n"; + } +} \ No newline at end of file diff --git a/src/Compile/FunctionCallCompiler.php b/src/Compile/FunctionCallCompiler.php new file mode 100644 index 0000000..107dd98 --- /dev/null +++ b/src/Compile/FunctionCallCompiler.php @@ -0,0 +1,79 @@ +getAttributes($compiler, $args); + unset($_attr['nocache']); + + $_paramsArray = $this->formatParamsArray($_attr); + $_params = 'array(' . implode(',', $_paramsArray) . ')'; + + + if ($functionHandler = $compiler->getSmarty()->getFunctionHandler($function)) { + + // not cacheable? + $compiler->tag_nocache = $compiler->tag_nocache || !$functionHandler->isCacheable(); + $output = "\$_smarty_tpl->getSmarty()->getFunctionHandler(" . var_export($function, true) . ")"; + $output .= "->handle($_params, \$_smarty_tpl)"; + } else { + $compiler->trigger_template_error("unknown function '{$function}'", null, true); + } + + if (!empty($parameter['modifierlist'])) { + $output = $compiler->compileModifier($parameter['modifierlist'], $output); + } + + return $output; + } +} diff --git a/src/Compile/Modifier/BCPluginWrapper.php b/src/Compile/Modifier/BCPluginWrapper.php new file mode 100644 index 0000000..0147651 --- /dev/null +++ b/src/Compile/Modifier/BCPluginWrapper.php @@ -0,0 +1,19 @@ +callback = $callback; + } + + /** + * @inheritDoc + */ + public function compile($params, \Smarty\Compiler\Template $compiler) { + return call_user_func($this->callback, $params, $compiler); + } +} \ No newline at end of file diff --git a/src/Compile/Modifier/Base.php b/src/Compile/Modifier/Base.php new file mode 100644 index 0000000..2ae5722 --- /dev/null +++ b/src/Compile/Modifier/Base.php @@ -0,0 +1,49 @@ +literal_compiler_param($params, 1, 'html'); + $char_set = $this->literal_compiler_param($params, 2, \Smarty\Smarty::$_CHARSET); + $double_encode = $this->literal_compiler_param($params, 3, true); + if (!$char_set) { + $char_set = \Smarty\Smarty::$_CHARSET; + } + switch ($esc_type) { + case 'html': + case 'force': + // in case of auto-escaping, and without the 'force' option, no double-escaping + if ($compiler->getSmarty()->escape_html && $esc_type != 'force') + return $params[0]; + // otherwise, escape the variable + return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . + var_export($double_encode, true) . ')'; + // no break + case 'htmlall': + $compiler->setRawOutput(true); + return 'htmlentities(mb_convert_encoding((string)' . $params[ 0 ] . ', \'UTF-8\', ' . + var_export($char_set, true) . '), ENT_QUOTES, \'UTF-8\', ' . + var_export($double_encode, true) . ')'; + // no break + case 'url': + $compiler->setRawOutput(true); + return 'rawurlencode((string)' . $params[ 0 ] . ')'; + case 'urlpathinfo': + $compiler->setRawOutput(true); + return 'str_replace("%2F", "/", rawurlencode((string)' . $params[ 0 ] . '))'; + case 'quotes': + $compiler->setRawOutput(true); + // escape unescaped single quotes + return 'preg_replace("%(?setRawOutput(true); + // escape quotes and backslashes, newlines, etc. + // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements + return 'strtr((string)' . + $params[ 0 ] . + ', array("\\\\" => "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", + "\\n" => "\\\n", " "<\/", " Smarty Compiler: ' . $this->message . ' <-- '; + } + + /** + * @param int $line + */ + public function setLine($line) { + $this->line = $line; + } + + /** + * The template source snippet relating to the error + * + * @type string|null + */ + public $source = null; + + /** + * The raw text of the error message + * + * @type string|null + */ + public $desc = null; + + /** + * The resource identifier or template name + * + * @type string|null + */ + public $template = null; +} diff --git a/src/Data.php b/src/Data.php new file mode 100644 index 0000000..6ae823d --- /dev/null +++ b/src/Data.php @@ -0,0 +1,521 @@ +smarty = $smarty; + if (is_object($_parent)) { + // when object set up back pointer + $this->parent = $_parent; + } elseif (is_array($_parent)) { + // set up variable values + foreach ($_parent as $_key => $_val) { + $this->assign($_key, $_val); + } + } elseif ($_parent !== null) { + throw new Exception('Wrong type for template variables'); + } + } + + /** + * assigns a Smarty variable + * + * @param array|string $tpl_var the template variable name(s) + * @param mixed $value the value to assign + * @param boolean $nocache if true any output of this variable will be not cached + * @param int $scope one of self::SCOPE_* constants + * + * @return Data current Data (or Smarty or \Smarty\Template) instance for + * chaining + */ + public function assign($tpl_var, $value = null, $nocache = false, $scope = null) + { + if (is_array($tpl_var)) { + foreach ($tpl_var as $_key => $_val) { + $this->assign($_key, $_val, $nocache, $scope); + } + return $this; + } + switch ($scope ?? $this->getDefaultScope()) { + case self::SCOPE_GLOBAL: + case self::SCOPE_SMARTY: + $this->getSmarty()->assign($tpl_var, $value); + break; + case self::SCOPE_TPL_ROOT: + $ptr = $this; + while (isset($ptr->parent) && ($ptr->parent instanceof Template)) { + $ptr = $ptr->parent; + } + $ptr->assign($tpl_var, $value); + break; + case self::SCOPE_ROOT: + $ptr = $this; + while (isset($ptr->parent) && !($ptr->parent instanceof Smarty)) { + $ptr = $ptr->parent; + } + $ptr->assign($tpl_var, $value); + break; + case self::SCOPE_PARENT: + if ($this->parent) { + $this->parent->assign($tpl_var, $value); + } else { + // assign local as fallback + $this->assign($tpl_var, $value); + } + break; + case self::SCOPE_LOCAL: + default: + if (isset($this->tpl_vars[$tpl_var])) { + $this->tpl_vars[$tpl_var]->setValue($value); + if ($nocache) { + $this->tpl_vars[$tpl_var]->setNocache(true); + } + } else { + $this->tpl_vars[$tpl_var] = new Variable($value, $nocache); + } + } + + return $this; + } + + /** + * appends values to template variables + * + * @param array|string $tpl_var the template variable name(s) + * @param mixed $value the value to append + * @param bool $merge flag if array elements shall be merged + * @param bool $nocache if true any output of this variable will + * be not cached + * + * @return Data + * @api Smarty::append() + */ + public function append($tpl_var, $value = null, $merge = false, $nocache = false) + { + if (is_array($tpl_var)) { + foreach ($tpl_var as $_key => $_val) { + $this->append($_key, $_val, $merge, $nocache); + } + } else { + + $newValue = $this->getValue($tpl_var) ?? []; + if (!is_array($newValue)) { + $newValue = (array) $newValue; + } + + if ($merge && is_array($value)) { + foreach ($value as $_mkey => $_mval) { + $newValue[$_mkey] = $_mval; + } + } else { + $newValue[] = $value; + } + + $this->assign($tpl_var, $newValue, $nocache); + } + return $this; + } + + /** + * assigns a global Smarty variable + * + * @param string $varName the global variable name + * @param mixed $value the value to assign + * @param boolean $nocache if true any output of this variable will be not cached + * + * @return Data + * @deprecated since 5.0 + */ + public function assignGlobal($varName, $value = null, $nocache = false) + { + trigger_error(__METHOD__ . " is deprecated. Use \\Smarty\\Smarty::assign() to assign a variable " . + " at the Smarty level.", E_USER_DEPRECATED); + return $this->getSmarty()->assign($varName, $value, $nocache); + } + + /** + * Returns a single or all template variables + * + * @param string $varName variable name or null + * @param bool $searchParents include parent templates? + * + * @return mixed variable value or or array of variables + * @api Smarty::getTemplateVars() + * + */ + public function getTemplateVars($varName = null, $searchParents = true) + { + if (isset($varName)) { + return $this->getValue($varName, $searchParents); + } + + return array_merge( + $this->parent && $searchParents ? $this->parent->getTemplateVars() : [], + array_map(function(Variable $var) { return $var->getValue(); }, $this->tpl_vars) + ); + } + + /** + * Wrapper for ::getVariable() + * + * @deprecated since 5.0 + * + * @param $varName + * @param $searchParents + * @param $errorEnable + * + * @return void + */ + public function _getVariable($varName, $searchParents = true, $errorEnable = true) { + trigger_error('Using ::_getVariable() to is deprecated and will be ' . + 'removed in a future release. Use getVariable() instead.', E_USER_DEPRECATED); + return $this->getVariable($varName, $searchParents, $errorEnable); + } + + /** + * Gets the object of a Smarty variable + * + * @param string $varName the name of the Smarty variable + * @param bool $searchParents search also in parent data + * @param bool $errorEnable + * + * @return Variable + */ + public function getVariable($varName, $searchParents = true, $errorEnable = true) { + if (isset($this->tpl_vars[$varName])) { + return $this->tpl_vars[$varName]; + } + + if ($searchParents && $this->parent) { + return $this->parent->getVariable($varName, $searchParents, $errorEnable); + } + + if ($errorEnable && $this->getSmarty()->error_unassigned) { + // force a notice + $x = $$varName; + } + return new UndefinedVariable(); + } + + /** + * Directly sets a complete Variable object in the variable with the given name. + * @param $varName + * @param Variable $variableObject + * + * @return void + */ + public function setVariable($varName, Variable $variableObject) { + $this->tpl_vars[$varName] = $variableObject; + } + + /** + * Indicates if given variable has been set. + * @param $varName + * + * @return bool + */ + public function hasVariable($varName): bool { + return !($this->getVariable($varName, true, false) instanceof UndefinedVariable); + } + + /** + * Returns the value of the Smarty\Variable given by $varName, or null if the variable does not exist. + * + * @param $varName + * @param bool $searchParents + * + * @return mixed|null + */ + public function getValue($varName, $searchParents = true) { + $variable = $this->getVariable($varName, $searchParents); + return isset($variable) ? $variable->getValue() : null; + } + + /** + * load config variables into template object + * + * @param array $new_config_vars + */ + public function assignConfigVars($new_config_vars, array $sections = []) { + + // copy global config vars + foreach ($new_config_vars['vars'] as $variable => $value) { + if ($this->getSmarty()->config_overwrite || !isset($this->config_vars[$variable])) { + $this->config_vars[$variable] = $value; + } else { + $this->config_vars[$variable] = array_merge((array)$this->config_vars[$variable], (array)$value); + } + } + + foreach ($sections as $tpl_section) { + if (isset($new_config_vars['sections'][$tpl_section])) { + foreach ($new_config_vars['sections'][$tpl_section]['vars'] as $variable => $value) { + if ($this->getSmarty()->config_overwrite || !isset($this->config_vars[$variable])) { + $this->config_vars[$variable] = $value; + } else { + $this->config_vars[$variable] = array_merge((array)$this->config_vars[$variable], (array)$value); + } + } + } + } + } + + /** + * Get Smarty object + * + * @return Smarty + */ + public function getSmarty() + { + return $this->smarty; + } + + /** + * clear the given assigned template variable(s). + * + * @param string|array $tpl_var the template variable(s) to clear + * + * @return Data + * + * @api Smarty::clearAssign() + */ + public function clearAssign($tpl_var) + { + if (is_array($tpl_var)) { + foreach ($tpl_var as $curr_var) { + unset($this->tpl_vars[ $curr_var ]); + } + } else { + unset($this->tpl_vars[ $tpl_var ]); + } + return $this; + } + + /** + * clear all the assigned template variables. + * + * @return Data + * + * @api Smarty::clearAllAssign() + */ + public function clearAllAssign() + { + $this->tpl_vars = array(); + return $this; + } + + /** + * clear a single or all config variables + * + * @param string|null $name variable name or null + * + * @return Data + * + * @api Smarty::clearConfig() + */ + public function clearConfig($name = null) + { + if (isset($name)) { + unset($this->config_vars[ $name ]); + } else { + $this->config_vars = array(); + } + return $this; + } + + /** + * Gets a config variable value + * + * @param string $varName the name of the config variable + * + * @return mixed the value of the config variable + * @throws Exception + */ + public function getConfigVariable($varName) + { + + if (isset($this->config_vars[$varName])) { + return $this->config_vars[$varName]; + } + + $returnValue = $this->parent ? $this->parent->getConfigVariable($varName) : null; + + if ($returnValue === null && $this->getSmarty()->error_unassigned) { + throw new Exception("Undefined variable $varName"); + } + + return $returnValue; + } + + public function hasConfigVariable($varName): bool { + try { + return $this->getConfigVariable($varName) !== null; + } catch (Exception $e) { + return false; + } + } + + /** + * Returns a single or all config variables + * + * @param string $varname variable name or null + * + * @return mixed variable value or or array of variables + * @throws Exception + * + * @api Smarty::getConfigVars() + */ + public function getConfigVars($varname = null) + { + if (isset($varname)) { + return $this->getConfigVariable($varname); + } + + return array_merge($this->parent ? $this->parent->getConfigVars() : [], $this->config_vars); + } + + /** + * load a config file, optionally load just selected sections + * + * @param string $config_file filename + * @param mixed $sections array of section names, single + * section or null + + * @returns $this + * @throws \Exception + * + * @api Smarty::configLoad() + */ + public function configLoad($config_file, $sections = null) + { + $template = $this->getSmarty()->doCreateTemplate($config_file, null, null, $this, null, null, true); + $template->caching = Smarty::CACHING_OFF; + $template->assign('sections', (array) $sections ?? []); + // trigger a call to $this->assignConfigVars + $template->fetch(); + return $this; + } + + /** + * Sets the default scope for new variables assigned in this template. + * @param int $scope + * + * @return void + */ + protected function setDefaultScope(int $scope) { + $this->defaultScope = $scope; + } + + /** + * Returns the default scope for new variables assigned in this template. + * @return int + */ + public function getDefaultScope(): int { + return $this->defaultScope; + } + + /** + * @return Data|Smarty|null + */ + public function getParent() { + return $this->parent; + } + + /** + * @param Data|Smarty|null $parent + */ + public function setParent($parent): void { + $this->parent = $parent; + } + + public function pushStack(): void { + $stackList = []; + foreach ($this->tpl_vars as $name => $variable) { + $stackList[$name] = clone $variable; // variables are stored in Variable objects + } + $this->_var_stack[] = $this->tpl_vars; + $this->tpl_vars = $stackList; + + $this->_config_stack[] = $this->config_vars; + } + + public function popStack(): void { + $this->tpl_vars = array_pop($this->_var_stack); + $this->config_vars = array_pop($this->_config_stack); + } +} diff --git a/src/sysplugins/smarty_internal_debug.php b/src/Debug.php similarity index 55% rename from src/sysplugins/smarty_internal_debug.php rename to src/Debug.php index da67904..ab1a887 100644 --- a/src/sysplugins/smarty_internal_debug.php +++ b/src/Debug.php @@ -1,34 +1,28 @@ _isSubTpl()) { $this->index++; @@ -64,39 +58,30 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * End logging of cache time * - * @param \Smarty_Internal_Template $template cached template + * @param Template $template cached template */ - public function end_template(Smarty_Internal_Template $template) + public function end_template(Template $template) { $key = $this->get_key($template); $this->template_data[ $this->index ][ $key ][ 'total_time' ] += microtime(true) - $this->template_data[ $this->index ][ $key ][ 'start_template_time' ]; - //$this->template_data[$this->index][$key]['properties'] = $template->properties; } /** * Start logging of compile time * - * @param \Smarty_Internal_Template $template + * @param Template $template */ - public function start_compile(Smarty_Internal_Template $template) + public function start_compile(Template $template) { static $_is_stringy = array('string' => true, 'eval' => true); - if (!empty($template->compiler->trace_uid)) { - $key = $template->compiler->trace_uid; + if (!empty($template->getCompiler()->trace_uid)) { + $key = $template->getCompiler()->trace_uid; if (!isset($this->template_data[ $this->index ][ $key ])) { - if (isset($_is_stringy[ $template->source->type ])) { - $this->template_data[ $this->index ][ $key ][ 'name' ] = - '\'' . substr($template->source->name, 0, 25) . '...\''; - } else { - $this->template_data[ $this->index ][ $key ][ 'name' ] = $template->source->filepath; - } - $this->template_data[ $this->index ][ $key ][ 'compile_time' ] = 0; - $this->template_data[ $this->index ][ $key ][ 'render_time' ] = 0; - $this->template_data[ $this->index ][ $key ][ 'cache_time' ] = 0; + $this->saveTemplateData($_is_stringy, $template, $key); } } else { - if (isset($this->ignore_uid[ $template->source->uid ])) { + if (isset($this->ignore_uid[ $template->getSource()->uid ])) { return; } $key = $this->get_key($template); @@ -107,14 +92,14 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * End logging of compile time * - * @param \Smarty_Internal_Template $template + * @param Template $template */ - public function end_compile(Smarty_Internal_Template $template) + public function end_compile(Template $template) { - if (!empty($template->compiler->trace_uid)) { - $key = $template->compiler->trace_uid; + if (!empty($template->getCompiler()->trace_uid)) { + $key = $template->getCompiler()->trace_uid; } else { - if (isset($this->ignore_uid[ $template->source->uid ])) { + if (isset($this->ignore_uid[ $template->getSource()->uid ])) { return; } $key = $this->get_key($template); @@ -126,9 +111,9 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * Start logging of render time * - * @param \Smarty_Internal_Template $template + * @param Template $template */ - public function start_render(Smarty_Internal_Template $template) + public function start_render(Template $template) { $key = $this->get_key($template); $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true); @@ -137,9 +122,9 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * End logging of compile time * - * @param \Smarty_Internal_Template $template + * @param Template $template */ - public function end_render(Smarty_Internal_Template $template) + public function end_render(Template $template) { $key = $this->get_key($template); $this->template_data[ $this->index ][ $key ][ 'render_time' ] += @@ -149,9 +134,9 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * Start logging of cache time * - * @param \Smarty_Internal_Template $template cached template + * @param Template $template cached template */ - public function start_cache(Smarty_Internal_Template $template) + public function start_cache(Template $template) { $key = $this->get_key($template); $this->template_data[ $this->index ][ $key ][ 'start_time' ] = microtime(true); @@ -160,9 +145,9 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * End logging of cache time * - * @param \Smarty_Internal_Template $template cached template + * @param Template $template cached template */ - public function end_cache(Smarty_Internal_Template $template) + public function end_cache(Template $template) { $key = $this->get_key($template); $this->template_data[ $this->index ][ $key ][ 'cache_time' ] += @@ -172,65 +157,52 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * Register template object * - * @param \Smarty_Internal_Template $template cached template + * @param Template $template cached template */ - public function register_template(Smarty_Internal_Template $template) + public function register_template(Template $template) { } /** * Register data object * - * @param \Smarty_Data $data data object + * @param Data $data data object */ - public static function register_data(Smarty_Data $data) + public static function register_data(Data $data) { } /** * Opens a window for the Smarty Debugging Console and display the data * - * @param Smarty_Internal_Template|Smarty $obj object to debug + * @param Template|Smarty $obj object to debug * @param bool $full * * @throws \Exception - * @throws \SmartyException + * @throws Exception */ - public function display_debug($obj, $full = false) + public function display_debug($obj, bool $full = false) { if (!$full) { $this->offset++; $savedIndex = $this->index; $this->index = 9999; } - $smarty = $obj->_getSmartyObj(); + $smarty = $obj->getSmarty(); // create fresh instance of smarty for displaying the debug console // to avoid problems if the application did overload the Smarty class $debObj = new Smarty(); // copy the working dirs from application $debObj->setCompileDir($smarty->getCompileDir()); - // init properties by hand as user may have edited the original Smarty class - $debObj->setPluginsDir(is_dir(__DIR__ . '/../plugins') ? __DIR__ . - '/../plugins' : $smarty->getPluginsDir()); - $debObj->force_compile = false; $debObj->compile_check = Smarty::COMPILECHECK_ON; - $debObj->left_delimiter = '{'; - $debObj->right_delimiter = '}'; $debObj->security_policy = null; $debObj->debugging = false; $debObj->debugging_ctrl = 'NONE'; $debObj->error_reporting = E_ALL & ~E_NOTICE; - $debObj->debug_tpl = - isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . __DIR__ . '/../debug.tpl'; - $debObj->registered_plugins = array(); + $debObj->debug_tpl = $smarty->debug_tpl ?? 'file:' . __DIR__ . '/debug.tpl'; $debObj->registered_resources = array(); - $debObj->registered_filters = array(); - $debObj->autoload_filters = array(); - $debObj->default_modifiers = array(); $debObj->escape_html = true; $debObj->caching = Smarty::CACHING_OFF; - $debObj->compile_id = null; - $debObj->cache_id = null; // prepare information of assigned variables $ptr = $this->get_debug_vars($obj); $_assigned_vars = $ptr->tpl_vars; @@ -238,15 +210,14 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data $_config_vars = $ptr->config_vars; ksort($_config_vars); $debugging = $smarty->debugging; - $templateName = $obj->source->type . ':' . $obj->source->name; + $templateName = $obj->getSource()->type . ':' . $obj->getSource()->name; $displayMode = $debugging === 2 || !$full; $offset = $this->offset * 50; - $_template = new Smarty_Internal_Template($debObj->debug_tpl, $debObj); - if ($obj->_isTplObj()) { + $_template = $debObj->doCreateTemplate($debObj->debug_tpl); + if ($obj instanceof Template) { $_template->assign('template_name', $templateName); - } - if ($obj->_objType === 1 || $full) { - $_template->assign('template_data', $this->template_data[ $this->index ]); + } elseif ($obj instanceof Smarty || $full) { + $_template->assign('template_data', $this->template_data[$this->index]); } else { $_template->assign('template_data', null); } @@ -267,22 +238,16 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * Recursively gets variables from all template/data scopes * - * @param Smarty_Internal_Template|Smarty_Data $obj object to debug + * @param \Smarty\Data $obj object to debug * - * @return StdClass + * @return \StdClass */ - public function get_debug_vars($obj) + private function get_debug_vars($obj) { $config_vars = array(); foreach ($obj->config_vars as $key => $var) { - $config_vars[ $key ][ 'value' ] = $var; - if ($obj->_isTplObj()) { - $config_vars[ $key ][ 'scope' ] = $obj->source->type . ':' . $obj->source->name; - } elseif ($obj->_isDataObj()) { - $tpl_vars[ $key ][ 'scope' ] = $obj->dataObjectName; - } else { - $config_vars[ $key ][ 'scope' ] = 'Smarty object'; - } + $config_vars[$key]['value'] = $var; + $config_vars[$key]['scope'] = get_class($obj) . ':' . spl_object_id($obj); } $tpl_vars = array(); foreach ($obj->tpl_vars as $key => $var) { @@ -301,13 +266,7 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data } } } - if ($obj->_isTplObj()) { - $tpl_vars[ $key ][ 'scope' ] = $obj->source->type . ':' . $obj->source->name; - } elseif ($obj->_isDataObj()) { - $tpl_vars[ $key ][ 'scope' ] = $obj->dataObjectName; - } else { - $tpl_vars[ $key ][ 'scope' ] = 'Smarty object'; - } + $tpl_vars[$key]['scope'] = get_class($obj) . ':' . spl_object_id($obj); } if (isset($obj->parent)) { $parent = $this->get_debug_vars($obj->parent); @@ -323,27 +282,6 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data } } $config_vars = array_merge($parent->config_vars, $config_vars); - } else { - foreach (Smarty::$global_tpl_vars as $key => $var) { - if (!array_key_exists($key, $tpl_vars)) { - foreach ($var as $varkey => $varvalue) { - if ($varkey === 'value') { - $tpl_vars[ $key ][ $varkey ] = $varvalue; - } else { - if ($varkey === 'nocache') { - if ($varvalue === true) { - $tpl_vars[ $key ][ $varkey ] = $varvalue; - } - } else { - if ($varkey !== 'scope' || $varvalue !== 0) { - $tpl_vars[ $key ][ 'attributes' ][ $varkey ] = $varvalue; - } - } - } - } - $tpl_vars[ $key ][ 'scope' ] = 'Global'; - } - } } return (object)array('tpl_vars' => $tpl_vars, 'config_vars' => $config_vars); } @@ -351,31 +289,20 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * Return key into $template_data for template * - * @param \Smarty_Internal_Template $template template object + * @param Template $template template object * * @return string key into $template_data */ - private function get_key(Smarty_Internal_Template $template) + private function get_key(Template $template) { static $_is_stringy = array('string' => true, 'eval' => true); - // calculate Uid if not already done - if ($template->source->uid === '') { - $template->source->filepath; - } - $key = $template->source->uid; + + $key = $template->getSource()->uid; if (isset($this->template_data[ $this->index ][ $key ])) { return $key; } else { - if (isset($_is_stringy[ $template->source->type ])) { - $this->template_data[ $this->index ][ $key ][ 'name' ] = - '\'' . substr($template->source->name, 0, 25) . '...\''; - } else { - $this->template_data[ $this->index ][ $key ][ 'name' ] = $template->source->filepath; - } - $this->template_data[ $this->index ][ $key ][ 'compile_time' ] = 0; - $this->template_data[ $this->index ][ $key ][ 'render_time' ] = 0; - $this->template_data[ $this->index ][ $key ][ 'cache_time' ] = 0; - $this->template_data[ $this->index ][ $key ][ 'total_time' ] = 0; + $this->saveTemplateData($_is_stringy, $template, $key); + $this->template_data[ $this->index ][ $key ][ 'total_time' ] = 0; return $key; } } @@ -383,15 +310,11 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data /** * Ignore template * - * @param \Smarty_Internal_Template $template + * @param Template $template */ - public function ignore(Smarty_Internal_Template $template) + public function ignore(Template $template) { - // calculate Uid if not already done - if ($template->source->uid === '') { - $template->source->filepath; - } - $this->ignore_uid[ $template->source->uid ] = true; + $this->ignore_uid[$template->getSource()->uid] = true; } /** @@ -425,4 +348,23 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data } } } + + /** + * @param array $_is_stringy + * @param Template $template + * @param string $key + * + * @return void + */ + private function saveTemplateData(array $_is_stringy, Template $template, string $key): void { + if (isset($_is_stringy[$template->getSource()->type])) { + $this->template_data[$this->index][$key]['name'] = + '\'' . substr($template->getSource()->name, 0, 25) . '...\''; + } else { + $this->template_data[$this->index][$key]['name'] = $template->getSource()->getResourceName(); + } + $this->template_data[$this->index][$key]['compile_time'] = 0; + $this->template_data[$this->index][$key]['render_time'] = 0; + $this->template_data[$this->index][$key]['cache_time'] = 0; + } } diff --git a/src/sysplugins/smarty_internal_errorhandler.php b/src/ErrorHandler.php similarity index 86% rename from src/sysplugins/smarty_internal_errorhandler.php rename to src/ErrorHandler.php index 4ddcfcd..05b1cb3 100644 --- a/src/sysplugins/smarty_internal_errorhandler.php +++ b/src/ErrorHandler.php @@ -1,22 +1,13 @@ propName} where propName is undefined. * @var bool @@ -78,14 +69,6 @@ class Smarty_Internal_ErrorHandler */ public function handleError($errno, $errstr, $errfile, $errline, $errcontext = []) { - - if ($this->allowUndefinedVars && preg_match( - '/^(Attempt to read property "value" on null|Trying to get property (\'value\' )?of non-object)/', - $errstr - )) { - return; // suppresses this error - } - if ($this->allowUndefinedProperties && preg_match( '/^(Undefined property)/', $errstr diff --git a/src/Exception.php b/src/Exception.php new file mode 100644 index 0000000..0f75f56 --- /dev/null +++ b/src/Exception.php @@ -0,0 +1,16 @@ + Smarty: ' . $this->message . ' <-- '; + } +} diff --git a/src/Extension/BCPluginsAdapter.php b/src/Extension/BCPluginsAdapter.php new file mode 100644 index 0000000..aa0eefe --- /dev/null +++ b/src/Extension/BCPluginsAdapter.php @@ -0,0 +1,229 @@ +smarty = $smarty; + } + + private function findPlugin($type, $name): ?array { + if (null !== $plugin = $this->smarty->getRegisteredPlugin($type, $name)) { + return $plugin; + } + + return null; + } + + public function getTagCompiler(string $tag): ?\Smarty\Compile\CompilerInterface { + + $plugin = $this->findPlugin(\Smarty\Smarty::PLUGIN_COMPILER, $tag); + if ($plugin === null) { + return null; + } + + if (is_callable($plugin[0])) { + $callback = $plugin[0]; + $cacheable = (bool) $plugin[1] ?? true; + return new TagPluginWrapper($callback, $cacheable); + } elseif (class_exists($plugin[0])) { + $compiler = new $plugin[0]; + if ($compiler instanceof CompilerInterface) { + return $compiler; + } + } + + return null; + } + + public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface { + $plugin = $this->findPlugin(\Smarty\Smarty::PLUGIN_FUNCTION, $functionName); + if ($plugin === null) { + return null; + } + $callback = $plugin[0]; + $cacheable = (bool) $plugin[1] ?? true; + + return new FunctionPluginWrapper($callback, $cacheable); + + } + + public function getBlockHandler(string $blockTagName): ?\Smarty\BlockHandler\BlockHandlerInterface { + $plugin = $this->findPlugin(\Smarty\Smarty::PLUGIN_BLOCK, $blockTagName); + if ($plugin === null) { + return null; + } + $callback = $plugin[0]; + $cacheable = (bool) $plugin[1] ?? true; + + return new BlockPluginWrapper($callback, $cacheable); + } + + public function getModifierCallback(string $modifierName) { + + $plugin = $this->findPlugin(\Smarty\Smarty::PLUGIN_MODIFIER, $modifierName); + if ($plugin === null) { + return null; + } + return $plugin[0]; + } + + public function getModifierCompiler(string $modifier): ?\Smarty\Compile\Modifier\ModifierCompilerInterface { + $plugin = $this->findPlugin(\Smarty\Smarty::PLUGIN_MODIFIERCOMPILER, $modifier); + if ($plugin === null) { + return null; + } + $callback = $plugin[0]; + + return new ModifierCompilerPluginWrapper($callback); + } + + /** + * @var array + */ + private $preFilters = []; + + public function getPreFilters(): array { + return $this->preFilters; + } + + public function addPreFilter(\Smarty\Filter\FilterInterface $filter) { + $this->preFilters[] = $filter; + } + + public function addCallableAsPreFilter(callable $callable, ?string $name = null) { + if ($name === null) { + $this->preFilters[] = new FilterPluginWrapper($callable); + } else { + $this->preFilters[$name] = new FilterPluginWrapper($callable); + } + } + + public function removePrefilter(string $name) { + unset($this->preFilters[$name]); + } + + /** + * @var array + */ + private $postFilters = []; + + public function getPostFilters(): array { + return $this->postFilters; + } + + public function addPostFilter(\Smarty\Filter\FilterInterface $filter) { + $this->postFilters[] = $filter; + } + + public function addCallableAsPostFilter(callable $callable, ?string $name = null) { + if ($name === null) { + $this->postFilters[] = new FilterPluginWrapper($callable); + } else { + $this->postFilters[$name] = new FilterPluginWrapper($callable); + } + } + + public function removePostFilter(string $name) { + unset($this->postFilters[$name]); + } + + + /** + * @var array + */ + private $outputFilters = []; + + public function getOutputFilters(): array { + return $this->outputFilters; + } + + public function addOutputFilter(\Smarty\Filter\FilterInterface $filter) { + $this->outputFilters[] = $filter; + } + + public function addCallableAsOutputFilter(callable $callable, ?string $name = null) { + if ($name === null) { + $this->outputFilters[] = new FilterPluginWrapper($callable); + } else { + $this->outputFilters[$name] = new FilterPluginWrapper($callable); + } + } + + public function removeOutputFilter(string $name) { + unset($this->outputFilters[$name]); + } + + public function loadPluginsFromDir(string $path) { + + foreach([ + 'function', + 'modifier', + 'block', + 'compiler', + 'prefilter', + 'postfilter', + 'outputfilter', + ] as $type) { + foreach (glob($path . $type . '.?*.php') as $filename) { + $pluginName = $this->getPluginNameFromFilename($filename); + if ($pluginName !== null) { + require_once $filename; + $functionOrClassName = 'smarty_' . $type . '_' . $pluginName; + if (function_exists($functionOrClassName) || class_exists($functionOrClassName)) { + $this->smarty->registerPlugin($type, $pluginName, $functionOrClassName, true, []); + } + } + } + } + + $type = 'resource'; + foreach (glob($path . $type . '.?*.php') as $filename) { + $pluginName = $this->getPluginNameFromFilename($filename); + if ($pluginName !== null) { + require_once $filename; + if (class_exists($className = 'smarty_' . $type . '_' . $pluginName)) { + $this->smarty->registerResource($pluginName, new $className()); + } + } + } + + $type = 'cacheresource'; + foreach (glob($path . $type . '.?*.php') as $filename) { + $pluginName = $this->getPluginNameFromFilename($filename); + if ($pluginName !== null) { + require_once $filename; + if (class_exists($className = 'smarty_' . $type . '_' . $pluginName)) { + $this->smarty->registerCacheResource($pluginName, new $className()); + } + } + } + + } + + /** + * @param $filename + * + * @return string|null + */ + private function getPluginNameFromFilename($filename) { + if (!preg_match('/.*\.([a-z_A-Z0-9]+)\.php$/',$filename,$matches)) { + return null; + } + return $matches[1]; + } + +} \ No newline at end of file diff --git a/src/Extension/Base.php b/src/Extension/Base.php new file mode 100644 index 0000000..b37b6ac --- /dev/null +++ b/src/Extension/Base.php @@ -0,0 +1,41 @@ +callback = $callback; + $this->modifierName = $modifierName; + } + + public function handle(...$params) { + try { + return call_user_func_array($this->callback, $params); + } catch (\ArgumentCountError $e) { + throw new Exception("Invalid number of arguments to modifier " . $this->modifierName); + } + } + +} \ No newline at end of file diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php new file mode 100644 index 0000000..a7c658d --- /dev/null +++ b/src/Extension/CoreExtension.php @@ -0,0 +1,49 @@ +modifiers[$modifier])) { + return $this->modifiers[$modifier]; + } + + switch ($modifier) { + case 'cat': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CatModifierCompiler(); break; + case 'count_characters': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountCharactersModifierCompiler(); break; + case 'count_paragraphs': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountParagraphsModifierCompiler(); break; + case 'count_sentences': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountSentencesModifierCompiler(); break; + case 'count_words': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountWordsModifierCompiler(); break; + case 'default': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\DefaultModifierCompiler(); break; + case 'empty': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EmptyModifierCompiler(); break; + case 'escape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EscapeModifierCompiler(); break; + case 'from_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\FromCharsetModifierCompiler(); break; + case 'indent': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IndentModifierCompiler(); break; + case 'is_array': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IsArrayModifierCompiler(); break; + case 'isset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IssetModifierCompiler(); break; + case 'json_encode': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\JsonEncodeModifierCompiler(); break; + case 'lower': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\LowerModifierCompiler(); break; + case 'nl2br': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\Nl2brModifierCompiler(); break; + case 'noprint': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\NoPrintModifierCompiler(); break; + case 'raw': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\RawModifierCompiler(); break; + case 'round': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\RoundModifierCompiler(); break; + case 'str_repeat': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StrRepeatModifierCompiler(); break; + case 'string_format': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StringFormatModifierCompiler(); break; + case 'strip': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StripModifierCompiler(); break; + case 'strip_tags': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StripTagsModifierCompiler(); break; + case 'strlen': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StrlenModifierCompiler(); break; + case 'substr': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\SubstrModifierCompiler(); break; + case 'to_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\ToCharsetModifierCompiler(); break; + case 'unescape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\UnescapeModifierCompiler(); break; + case 'upper': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\UpperModifierCompiler(); break; + case 'wordwrap': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\WordWrapModifierCompiler(); break; + } + + return $this->modifiers[$modifier] ?? null; + } + + public function getModifierCallback(string $modifierName) { + switch ($modifierName) { + case 'capitalize': return [$this, 'smarty_modifier_capitalize']; + case 'count': return [$this, 'smarty_modifier_count']; + case 'date_format': return [$this, 'smarty_modifier_date_format']; + case 'debug_print_var': return [$this, 'smarty_modifier_debug_print_var']; + case 'escape': return [$this, 'smarty_modifier_escape']; + case 'explode': return [$this, 'smarty_modifier_explode']; + case 'implode': return [$this, 'smarty_modifier_implode']; + case 'in_array': return [$this, 'smarty_modifier_in_array']; + case 'join': return [$this, 'smarty_modifier_join']; + case 'mb_wordwrap': return [$this, 'smarty_modifier_mb_wordwrap']; + case 'number_format': return [$this, 'smarty_modifier_number_format']; + case 'regex_replace': return [$this, 'smarty_modifier_regex_replace']; + case 'replace': return [$this, 'smarty_modifier_replace']; + case 'spacify': return [$this, 'smarty_modifier_spacify']; + case 'split': return [$this, 'smarty_modifier_split']; + case 'truncate': return [$this, 'smarty_modifier_truncate']; + } + return null; + } + + public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface { + + if (isset($this->functionHandlers[$functionName])) { + return $this->functionHandlers[$functionName]; + } + + switch ($functionName) { + case 'count': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Count(); break; + case 'counter': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Counter(); break; + case 'cycle': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Cycle(); break; + case 'fetch': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Fetch(); break; + case 'html_checkboxes': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlCheckboxes(); break; + case 'html_image': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlImage(); break; + case 'html_options': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlOptions(); break; + case 'html_radios': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlRadios(); break; + case 'html_select_date': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlSelectDate(); break; + case 'html_select_time': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlSelectTime(); break; + case 'html_table': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlTable(); break; + case 'mailto': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Mailto(); break; + case 'math': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Math(); break; + case 'popup_init': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\PopupInit(); break; + case 'popup': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Popup(); break; + } + + return $this->functionHandlers[$functionName] ?? null; + } + + public function getBlockHandler(string $blockTagName): ?\Smarty\BlockHandler\BlockHandlerInterface { + + switch ($blockTagName) { + case 'textformat': $this->blockHandlers[$blockTagName] = new \Smarty\BlockHandler\TextFormat(); break; + case 't': $this->blockHandlers[$blockTagName] = new \Smarty\BlockHandler\T(); break; + } + + return $this->blockHandlers[$blockTagName] ?? null; + } + + /** + * Smarty spacify modifier plugin + * Type: modifier + * Name: spacify + * Purpose: add spaces between characters in a string + * + * @author Monte Ohrt + * + * @param string $string input string + * @param string $spacify_char string to insert between characters. + * + * @return string + */ + public function smarty_modifier_spacify($string, $spacify_char = ' ') + { + // well… what about charsets besides latin and UTF-8? + return implode($spacify_char, preg_split('//' . \Smarty\Smarty::$_UTF8_MODIFIER, $string, -1, PREG_SPLIT_NO_EMPTY)); + } + + /** + * Smarty capitalize modifier plugin + * Type: modifier + * Name: capitalize + * Purpose: capitalize words in the string + * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }} + * + * @param string $string string to capitalize + * @param boolean $uc_digits also capitalize "x123" to "X123" + * @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa" + * + * @return string capitalized string + * @author Monte Ohrt + * @author Rodney Rehm + */ + public function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) + { + $string = (string) $string; + + if ($lc_rest) { + // uppercase (including hyphenated words) + $upper_string = mb_convert_case($string, MB_CASE_TITLE, \Smarty\Smarty::$_CHARSET); + } else { + // uppercase word breaks + $upper_string = preg_replace_callback( + "!(^|[^\p{L}'])([\p{Ll}])!S" . \Smarty\Smarty::$_UTF8_MODIFIER, + function ($matches) { + return stripslashes($matches[1]) . + mb_convert_case(stripslashes($matches[2]), MB_CASE_UPPER, \Smarty\Smarty::$_CHARSET); + }, + $string + ); + } + // check uc_digits case + if (!$uc_digits) { + if (preg_match_all( + "!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . \Smarty\Smarty::$_UTF8_MODIFIER, + $string, + $matches, + PREG_OFFSET_CAPTURE + ) + ) { + foreach ($matches[ 1 ] as $match) { + $upper_string = + substr_replace( + $upper_string, + mb_strtolower($match[ 0 ], \Smarty\Smarty::$_CHARSET), + $match[ 1 ], + strlen($match[ 0 ]) + ); + } + } + } + $upper_string = + preg_replace_callback( + "!((^|\s)['\"])(\w)!" . \Smarty\Smarty::$_UTF8_MODIFIER, + function ($matches) { + return stripslashes( + $matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 3 ]), + MB_CASE_UPPER, + \Smarty\Smarty::$_CHARSET + ); + }, + $upper_string + ); + return $upper_string; + } + + /** + * Smarty count modifier plugin + * Type: modifier + * Name: count + * Purpose: counts all elements in an array or in a Countable object + * Input: + * - Countable|array: array or object to count + * - mode: int defaults to 0 for normal count mode, if set to 1 counts recursive + * + * @param mixed $arrayOrObject input array/object + * @param int $mode count mode + * + * @return int + */ + public function smarty_modifier_count($arrayOrObject, $mode = 0) { + /* + * @see https://www.php.net/count + * > Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface, + * > 1 would be returned, unless value was null, in which case 0 would be returned. + */ + + if ($arrayOrObject instanceof \Countable || is_array($arrayOrObject)) { + return count($arrayOrObject, (int) $mode); + } elseif ($arrayOrObject === null) { + return 0; + } + return 1; + } + + /** + * Smarty date_format modifier plugin + * Type: modifier + * Name: date_format + * Purpose: format datestamps via strftime + * Input: + * - string: input date string + * - format: strftime format for output + * - default_date: default date if $string is empty + * + * @author Monte Ohrt + * + * @param string $string input date string + * @param string $format strftime format for output + * @param string $default_date default date if $string is empty + * @param string $formatter either 'strftime' or 'auto' + * + * @return string |void + * @uses smarty_make_timestamp() + */ + public function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') + { + if ($format === null) { + $format = \Smarty\Smarty::$_DATE_FORMAT; + } + + if (!empty($string) && $string !== '0000-00-00' && $string !== '0000-00-00 00:00:00') { + $timestamp = smarty_make_timestamp($string); + } elseif (!empty($default_date)) { + $timestamp = smarty_make_timestamp($default_date); + } else { + return; + } + if ($formatter === 'strftime' || ($formatter === 'auto' && strpos($format, '%') !== false)) { + if (\Smarty\Smarty::$_IS_WINDOWS) { + $_win_from = array( + '%D', + '%h', + '%n', + '%r', + '%R', + '%t', + '%T' + ); + $_win_to = array( + '%m/%d/%y', + '%b', + "\n", + '%I:%M:%S %p', + '%H:%M', + "\t", + '%H:%M:%S' + ); + if (strpos($format, '%e') !== false) { + $_win_from[] = '%e'; + $_win_to[] = sprintf('%\' 2d', date('j', $timestamp)); + } + if (strpos($format, '%l') !== false) { + $_win_from[] = '%l'; + $_win_to[] = sprintf('%\' 2d', date('h', $timestamp)); + } + $format = str_replace($_win_from, $_win_to, $format); + } + // @ to suppress deprecation errors when running in PHP8.1 or higher. + return @strftime($format, $timestamp); + } else { + return date($format, $timestamp); + } + } + + /** + * Smarty debug_print_var modifier plugin + * Type: modifier + * Name: debug_print_var + * Purpose: formats variable contents for display in the console + * + * @author Monte Ohrt + * + * @param array|object $var variable to be formatted + * @param int $max maximum recursion depth if $var is an array or object + * @param int $length maximum string length if $var is a string + * @param int $depth actual recursion depth + * @param array $objects processed objects in actual depth to prevent recursive object processing + * + * @return string + */ + public function smarty_modifier_debug_print_var($var, $max = 10, $length = 40, $depth = 0, $objects = array()) + { + $_replace = array("\n" => '\n', "\r" => '\r', "\t" => '\t'); + switch (gettype($var)) { + case 'array': + $results = 'Array (' . count($var) . ')'; + if ($depth === $max) { + break; + } + foreach ($var as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) . '' . strtr($curr_key, $_replace) . + ' => ' . + $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); + $depth--; + } + break; + case 'object': + $object_vars = get_object_vars($var); + $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; + if (in_array($var, $objects)) { + $results .= ' called recursive'; + break; + } + if ($depth === $max) { + break; + } + $objects[] = $var; + foreach ($object_vars as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) . ' ->' . strtr($curr_key, $_replace) . + ' = ' . $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); + $depth--; + } + break; + case 'boolean': + case 'NULL': + case 'resource': + if (true === $var) { + $results = 'true'; + } elseif (false === $var) { + $results = 'false'; + } elseif (null === $var) { + $results = 'null'; + } else { + $results = htmlspecialchars((string)$var); + } + $results = '' . $results . ''; + break; + case 'integer': + case 'float': + $results = htmlspecialchars((string)$var); + break; + case 'string': + $results = strtr($var, $_replace); + if (mb_strlen($var, \Smarty\Smarty::$_CHARSET) > $length) { + $results = mb_substr($var, 0, $length - 3, \Smarty\Smarty::$_CHARSET) . '...'; + } + $results = htmlspecialchars('"' . $results . '"', ENT_QUOTES, \Smarty\Smarty::$_CHARSET); + break; + case 'unknown type': + default: + $results = strtr((string)$var, $_replace); + if (mb_strlen($results, \Smarty\Smarty::$_CHARSET) > $length) { + $results = mb_substr($results, 0, $length - 3, \Smarty\Smarty::$_CHARSET) . '...'; + } + $results = htmlspecialchars($results, ENT_QUOTES, \Smarty\Smarty::$_CHARSET); + } + return $results; + } + + /** + * Smarty escape modifier plugin + * Type: modifier + * Name: escape + * Purpose: escape string for output + * + * @author Monte Ohrt + * + * @param string $string input string + * @param string $esc_type escape type + * @param string $char_set character set, used for htmlspecialchars() or htmlentities() + * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() + * + * @return string escaped input string + */ + public function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) + { + if (!$char_set) { + $char_set = \Smarty\Smarty::$_CHARSET; + } + + $string = (string)$string; + + switch ($esc_type) { + case 'html': + return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); + // no break + case 'htmlall': + $string = mb_convert_encoding($string, 'UTF-8', $char_set); + return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode); + // no break + case 'url': + return rawurlencode($string); + case 'urlpathinfo': + return str_replace('%2F', '/', rawurlencode($string)); + case 'quotes': + // escape unescaped single quotes + return preg_replace("%(?mb_to_unicode($string, \Smarty\Smarty::$_CHARSET) as $unicode) { + $return .= '&#x' . strtoupper(dechex($unicode)) . ';'; + } + return $return; + case 'decentity': + $return = ''; + foreach ($this->mb_to_unicode($string, \Smarty\Smarty::$_CHARSET) as $unicode) { + $return .= '&#' . $unicode . ';'; + } + return $return; + case 'javascript': + // escape quotes and backslashes, newlines, etc. + return strtr( + $string, + array( + '\\' => '\\\\', + "'" => "\\'", + '"' => '\\"', + "\r" => '\\r', + "\n" => '\\n', + ' '<\/', + // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements + '#is', + $source, + $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + ) + ) { + foreach ($matches as $match) { + $store[] = $match[ 0 ][ 0 ]; + $_length = strlen($match[ 0 ][ 0 ]); + $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; + $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); + $_offset += $_length - strlen($replace); + $_store++; + } + } + // Strip all HTML-Comments + // yes, even the ones in '; + } elseif ($encode === 'javascript_charcode') { + for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { + $ord[] = ord($string[$x]); + } + return ''; + } elseif ($encode === 'hex') { + preg_match('!^(.*)(\?.*)$!', $address, $match); + if (!empty($match[2])) { + trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.", E_USER_WARNING); + return; + } + $address_encode = ''; + for ($x = 0, $_length = strlen($address); $x < $_length; $x++) { + if (preg_match('!\w!' . \Smarty\Smarty::$_UTF8_MODIFIER, $address[$x])) { + $address_encode .= '%' . bin2hex($address[$x]); + } else { + $address_encode .= $address[$x]; + } + } + $text_encode = ''; + for ($x = 0, $_length = strlen($text); $x < $_length; $x++) { + $text_encode .= '&#x' . bin2hex($text[$x]) . ';'; + } + $mailto = "mailto:"; + return '' . $text_encode . ''; + } else { + // no encoding + return $string; + } + } +} diff --git a/src/FunctionHandler/Math.php b/src/FunctionHandler/Math.php new file mode 100644 index 0000000..23ef925 --- /dev/null +++ b/src/FunctionHandler/Math.php @@ -0,0 +1,140 @@ + + * + * @param array $params parameters + * @param Template $template template object + * + * @return string|null + */ +class Math extends Base { + + public function handle($params, Template $template) { + static $_allowed_funcs = + [ + 'int' => true, + 'abs' => true, + 'ceil' => true, + 'acos' => true, + 'acosh' => true, + 'cos' => true, + 'cosh' => true, + 'deg2rad' => true, + 'rad2deg' => true, + 'exp' => true, + 'floor' => true, + 'log' => true, + 'log10' => true, + 'max' => true, + 'min' => true, + 'pi' => true, + 'pow' => true, + 'rand' => true, + 'round' => true, + 'asin' => true, + 'asinh' => true, + 'sin' => true, + 'sinh' => true, + 'sqrt' => true, + 'srand' => true, + 'atan' => true, + 'atanh' => true, + 'tan' => true, + 'tanh' => true + ]; + + // be sure equation parameter is present + if (empty($params['equation'])) { + trigger_error("math: missing equation parameter", E_USER_WARNING); + return; + } + $equation = $params['equation']; + + // Remove whitespaces + $equation = preg_replace('/\s+/', '', $equation); + + // Adapted from https://www.php.net/manual/en/function.eval.php#107377 + $number = '-?(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number + $functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))'; + $operators = '[,+\/*\^%-]'; // Allowed math operators + $regexp = '/^((' . $number . '|' . $functionsOrVars . '|(' . $functionsOrVars . '\s*\((?1)*\)|\((?1)*\)))(?:' . $operators . '(?1))?)+$/'; + + if (!preg_match($regexp, $equation)) { + trigger_error("math: illegal characters", E_USER_WARNING); + return; + } + + // make sure parenthesis are balanced + if (substr_count($equation, '(') !== substr_count($equation, ')')) { + trigger_error("math: unbalanced parenthesis", E_USER_WARNING); + return; + } + + // disallow backticks + if (strpos($equation, '`') !== false) { + trigger_error("math: backtick character not allowed in equation", E_USER_WARNING); + return; + } + + // also disallow dollar signs + if (strpos($equation, '$') !== false) { + trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING); + return; + } + foreach ($params as $key => $val) { + if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') { + // make sure value is not empty + if (strlen($val) === 0) { + trigger_error("math: parameter '{$key}' is empty", E_USER_WARNING); + return; + } + if (!is_numeric($val)) { + trigger_error("math: parameter '{$key}' is not numeric", E_USER_WARNING); + return; + } + } + } + // match all vars in equation, make sure all are passed + preg_match_all('!(?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)!', $equation, $match); + foreach ($match[1] as $curr_var) { + if ($curr_var && !isset($params[$curr_var]) && !isset($_allowed_funcs[$curr_var])) { + trigger_error( + "math: function call '{$curr_var}' not allowed, or missing parameter '{$curr_var}'", + E_USER_WARNING + ); + return; + } + } + foreach ($params as $key => $val) { + if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') { + $equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation); + } + } + $smarty_math_result = null; + eval("\$smarty_math_result = " . $equation . ";"); + + if (empty($params['format'])) { + if (empty($params['assign'])) { + return $smarty_math_result; + } else { + $template->assign($params['assign'], $smarty_math_result); + } + } else { + if (empty($params['assign'])) { + printf($params['format'], $smarty_math_result); + } else { + $template->assign($params['assign'], sprintf($params['format'], $smarty_math_result)); + } + } + } +} diff --git a/src/FunctionHandler/Popup.php b/src/FunctionHandler/Popup.php new file mode 100644 index 0000000..bef05b5 --- /dev/null +++ b/src/FunctionHandler/Popup.php @@ -0,0 +1,123 @@ + + * Name: popup_init
+ * Purpose: initialize overlib + * @author Clemens Schwaighofer + * @param array + * @param Smarty + * @return string + */ + public function handle($params, Template $template) + { + $append = ''; + foreach ($params as $_key => $_value) { + switch ($_key) { + case 'text': + case 'trigger': + case 'function': + case 'inarray': + $$_key = (string)$_value; + if ($_key == 'function' || $_key == 'inarray') { + $append .= ',' . strtoupper($_key) . ",'$_value'"; + } + break; + + case 'caption': + case 'closetext': + case 'status': + $append .= ',' . strtoupper($_key) . ",'" . str_replace("'", "\'", $_value) . "'"; + break; + + case 'fgcolor': + case 'bgcolor': + case 'textcolor': + case 'capcolor': + case 'closecolor': + case 'textfont': + case 'captionfont': + case 'closefont': + case 'fgbackground': + case 'bgbackground': + case 'caparray': + case 'capicon': + case 'background': + case 'frame': + $append .= ',' . strtoupper($_key) . ",'$_value'"; + break; + + case 'textsize': + case 'captionsize': + case 'closesize': + case 'width': + case 'height': + case 'border': + case 'offsetx': + case 'offsety': + case 'snapx': + case 'snapy': + case 'fixx': + case 'fixy': + case 'padx': + case 'pady': + case 'timeout': + case 'delay': + $append .= ',' . strtoupper($_key) . ",$_value"; + break; + + case 'sticky': + case 'left': + case 'right': + case 'center': + case 'above': + case 'below': + case 'noclose': + case 'autostatus': + case 'autostatuscap': + case 'fullhtml': + case 'hauto': + case 'vauto': + case 'mouseoff': + case 'followmouse': + case 'closeclick': + if ($_value) { + $append .= ',' . strtoupper($_key); + } + break; + + default: + trigger_error("[popup] unknown parameter $_key", E_USER_WARNING); + } + } + + if (empty($text) && !isset($inarray) && empty($function)) { + trigger_error("overlib: attribute 'text' or 'inarray' or 'function' required"); + return false; + } + + if (empty($trigger)) { + $trigger = "onmouseover"; + } + + $retval = $trigger + . '="return overlib(\'' . preg_replace(array("!'!", "![\r\n]!"), array("\'", '\r'), $text) . '\''; + $retval .= $append . ');"'; + if ($trigger == 'onmouseover') { + $retval .= ' onmouseout="nd();"'; + } + + + return $retval; + } +} + +// __END__ diff --git a/src/FunctionHandler/PopupInit.php b/src/FunctionHandler/PopupInit.php new file mode 100644 index 0000000..538682b --- /dev/null +++ b/src/FunctionHandler/PopupInit.php @@ -0,0 +1,36 @@ + + * Name: popup_init
+ * Purpose: initialize overlib + * @author Clemens Schwaighofer + * @param array + * @param Smarty + * @return string + */ + public function handle($params, Template $template) + { + if (empty($params['src'])) { + trigger_error("popup_init: missing src parameter"); + return false; + } + return str_replace( + ["{SCRIPT_SOURCE}"], + [$params['src']], + << + HTML + ); + } +} + +// __END__ diff --git a/src/sysplugins/smarty_internal_configfilelexer.php b/src/Lexer/ConfigfileLexer.php similarity index 57% rename from src/sysplugins/smarty_internal_configfilelexer.php rename to src/Lexer/ConfigfileLexer.php index afb3efc..d592c82 100644 --- a/src/sysplugins/smarty_internal_configfilelexer.php +++ b/src/Lexer/ConfigfileLexer.php @@ -1,111 +1,117 @@ 'START', 2 => 'VALUE', 3 => 'NAKED_STRING_VALUE', 4 => 'COMMENT', 5 => 'SECTION', 6 => 'TRIPPLE' - ); + public $state_name = array(1 => 'START', 2 => 'VALUE', 3 => 'NAKED_STRING_VALUE', 4 => 'COMMENT', 5 => 'SECTION', 6 => 'TRIPPLE'); + + /** + * storage for assembled token patterns + * + * @var string + */ + private $yy_global_pattern1 = null; + private $yy_global_pattern2 = null; + private $yy_global_pattern3 = null; + private $yy_global_pattern4 = null; + private $yy_global_pattern5 = null; + private $yy_global_pattern6 = null; /** * token names @@ -115,54 +121,19 @@ class Smarty_Internal_Configfilelexer public $smarty_token_names = array( // Text for parser error messages ); - /** - * compiler object - * - * @var Smarty_Internal_Config_File_Compiler - */ - private $compiler = null; - - /** - * copy of config_booleanize - * - * @var bool - */ - private $configBooleanize = false; - - /** - * storage for assembled token patterns - * - * @var string - */ - private $yy_global_pattern1 = null; - - private $yy_global_pattern2 = null; - - private $yy_global_pattern3 = null; - - private $yy_global_pattern4 = null; - - private $yy_global_pattern5 = null; - - private $yy_global_pattern6 = null; - - private $_yy_state = 1; - - private $_yy_stack = array(); - /** * constructor * * @param string $data template source - * @param Smarty_Internal_Config_File_Compiler $compiler + * @param \Smarty\Compiler\Configfile $compiler */ - public function __construct($data, Smarty_Internal_Config_File_Compiler $compiler) + public function __construct($data, \Smarty\Compiler\Configfile $compiler) { $this->data = $data . "\n"; //now all lines are \n-terminated $this->dataLength = strlen($data); $this->counter = 0; if (preg_match('/^\xEF\xBB\xBF/', $this->data, $match)) { - $this->counter += strlen($match[ 0 ]); + $this->counter += strlen($match[0]); } $this->line = 1; $this->compiler = $compiler; @@ -170,10 +141,9 @@ class Smarty_Internal_Configfilelexer $this->configBooleanize = $this->smarty->config_booleanize; } - public function replace($input) - { + public function replace ($input) { return $input; - } // end function + } public function PrintTrace() { @@ -181,6 +151,11 @@ class Smarty_Internal_Configfilelexer $this->yyTracePrompt = '
'; } + + + private $_yy_state = 1; + private $_yy_stack = array(); + public function yylex() { return $this->{'yylex' . $this->_yy_state}(); @@ -189,85 +164,61 @@ class Smarty_Internal_Configfilelexer public function yypushstate($state) { if ($this->yyTraceFILE) { - fprintf( - $this->yyTraceFILE, - "%sState push %s\n", - $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state - ); + fprintf($this->yyTraceFILE, "%sState push %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } array_push($this->_yy_stack, $this->_yy_state); $this->_yy_state = $state; if ($this->yyTraceFILE) { - fprintf( - $this->yyTraceFILE, - "%snew State %s\n", - $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state - ); + fprintf($this->yyTraceFILE, "%snew State %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } } public function yypopstate() { - if ($this->yyTraceFILE) { - fprintf( - $this->yyTraceFILE, - "%sState pop %s\n", - $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state - ); + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sState pop %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } - $this->_yy_state = array_pop($this->_yy_stack); + $this->_yy_state = array_pop($this->_yy_stack); if ($this->yyTraceFILE) { - fprintf( - $this->yyTraceFILE, - "%snew State %s\n", - $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state - ); + fprintf($this->yyTraceFILE, "%snew State %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } + } public function yybegin($state) { - $this->_yy_state = $state; + $this->_yy_state = $state; if ($this->yyTraceFILE) { - fprintf( - $this->yyTraceFILE, - "%sState set %s\n", - $this->yyTracePrompt, - isset($this->state_name[ $this->_yy_state ]) ? $this->state_name[ $this->_yy_state ] : $this->_yy_state - ); + fprintf($this->yyTraceFILE, "%sState set %s\n", $this->yyTracePrompt, isset($this->state_name[$this->_yy_state]) ? $this->state_name[$this->_yy_state] : $this->_yy_state); } } + + + public function yylex1() { if (!isset($this->yy_global_pattern1)) { - $this->yy_global_pattern1 = - $this->replace("/\G(#|;)|\G(\\[)|\G(\\])|\G(=)|\G([ \t\r]+)|\G(\n)|\G([0-9]*[a-zA-Z_]\\w*)|\G([\S\s])/isS"); + $this->yy_global_pattern1 = $this->replace("/\G(#|;)|\G(\\[)|\G(\\])|\G(=)|\G([ \t\r]+)|\G(\n)|\G([0-9]*[a-zA-Z_]\\w*)|\G([\S\s])/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern1, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern1,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr( - $this->data, - $this->counter, - 5 - ) . '... state START'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state START'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -285,89 +236,91 @@ class Smarty_Internal_Configfilelexer } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { + } } else { throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[ $this->counter ]); + ': ' . $this->data[$this->counter]); } break; } while (true); - } - public function yy_r1_1() - { - $this->token = Smarty_Internal_Configfileparser::TPC_COMMENTSTART; - $this->yypushstate(self::COMMENT); - } - - public function yy_r1_2() - { - $this->token = Smarty_Internal_Configfileparser::TPC_OPENB; - $this->yypushstate(self::SECTION); - } - - public function yy_r1_3() - { - $this->token = Smarty_Internal_Configfileparser::TPC_CLOSEB; - } - - public function yy_r1_4() - { - $this->token = Smarty_Internal_Configfileparser::TPC_EQUAL; - $this->yypushstate(self::VALUE); } // end function + + const START = 1; + public function yy_r1_1() + { + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_COMMENTSTART; + $this->yypushstate(self::COMMENT); + } + public function yy_r1_2() + { + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_OPENB; + $this->yypushstate(self::SECTION); + } + public function yy_r1_3() + { + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_CLOSEB; + } + public function yy_r1_4() + { + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_EQUAL; + $this->yypushstate(self::VALUE); + } public function yy_r1_5() { - return false; - } + return false; + } public function yy_r1_6() { - $this->token = Smarty_Internal_Configfileparser::TPC_NEWLINE; - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NEWLINE; + } public function yy_r1_7() { - $this->token = Smarty_Internal_Configfileparser::TPC_ID; - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_ID; + } public function yy_r1_8() { - $this->token = Smarty_Internal_Configfileparser::TPC_OTHER; + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_OTHER; } + + public function yylex2() { if (!isset($this->yy_global_pattern2)) { - $this->yy_global_pattern2 = - $this->replace("/\G([ \t\r]+)|\G(\\d+\\.\\d+(?=[ \t\r]*[\n#;]))|\G(\\d+(?=[ \t\r]*[\n#;]))|\G(\"\"\")|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*'(?=[ \t\r]*[\n#;]))|\G(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"(?=[ \t\r]*[\n#;]))|\G([a-zA-Z]+(?=[ \t\r]*[\n#;]))|\G([^\n]+?(?=[ \t\r]*\n))|\G(\n)/isS"); + $this->yy_global_pattern2 = $this->replace("/\G([ \t\r]+)|\G(\\d+\\.\\d+(?=[ \t\r]*[\n#;]))|\G(\\d+(?=[ \t\r]*[\n#;]))|\G(\"\"\")|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*'(?=[ \t\r]*[\n#;]))|\G(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"(?=[ \t\r]*[\n#;]))|\G([a-zA-Z]+(?=[ \t\r]*[\n#;]))|\G([^\n]+?(?=[ \t\r]*\n))|\G(\n)/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern2, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern2,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr( - $this->data, - $this->counter, - 5 - ) . '... state VALUE'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state VALUE'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -385,80 +338,84 @@ class Smarty_Internal_Configfilelexer } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { + } } else { throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[ $this->counter ]); + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const VALUE = 2; public function yy_r2_1() { - return false; - } + return false; + } public function yy_r2_2() { - $this->token = Smarty_Internal_Configfileparser::TPC_FLOAT; - $this->yypopstate(); - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_FLOAT; + $this->yypopstate(); + } public function yy_r2_3() { - $this->token = Smarty_Internal_Configfileparser::TPC_INT; - $this->yypopstate(); - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_INT; + $this->yypopstate(); + } public function yy_r2_4() { - $this->token = Smarty_Internal_Configfileparser::TPC_TRIPPLE_QUOTES; - $this->yypushstate(self::TRIPPLE); - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_TRIPPLE_QUOTES; + $this->yypushstate(self::TRIPPLE); + } public function yy_r2_5() { - $this->token = Smarty_Internal_Configfileparser::TPC_SINGLE_QUOTED_STRING; - $this->yypopstate(); - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_SINGLE_QUOTED_STRING; + $this->yypopstate(); + } public function yy_r2_6() { - $this->token = Smarty_Internal_Configfileparser::TPC_DOUBLE_QUOTED_STRING; - $this->yypopstate(); - } // end function + $this->token = \Smarty\Parser\ConfigfileParser::TPC_DOUBLE_QUOTED_STRING; + $this->yypopstate(); + } public function yy_r2_7() { - if (!$this->configBooleanize || - !in_array(strtolower($this->value), array('true', 'false', 'on', 'off', 'yes', 'no'))) { - $this->yypopstate(); - $this->yypushstate(self::NAKED_STRING_VALUE); - return true; //reprocess in new state - } else { - $this->token = Smarty_Internal_Configfileparser::TPC_BOOL; - $this->yypopstate(); - } - } + if (!$this->configBooleanize || !in_array(strtolower($this->value), array('true', 'false', 'on', 'off', 'yes', 'no')) ) { + $this->yypopstate(); + $this->yypushstate(self::NAKED_STRING_VALUE); + return true; //reprocess in new state + } else { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_BOOL; + $this->yypopstate(); + } + } public function yy_r2_8() { - $this->token = Smarty_Internal_Configfileparser::TPC_NAKED_STRING; - $this->yypopstate(); - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + $this->yypopstate(); + } public function yy_r2_9() { - $this->token = Smarty_Internal_Configfileparser::TPC_NAKED_STRING; - $this->value = ''; - $this->yypopstate(); - } // end function + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + $this->value = ''; + $this->yypopstate(); + } + + public function yylex3() { @@ -468,23 +425,21 @@ class Smarty_Internal_Configfilelexer if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern3, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern3,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr( - $this->data, - $this->counter, - 5 - ) . '... state NAKED_STRING_VALUE'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state NAKED_STRING_VALUE'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -502,26 +457,31 @@ class Smarty_Internal_Configfilelexer } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { + } } else { throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[ $this->counter ]); + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const NAKED_STRING_VALUE = 3; public function yy_r3_1() { - $this->token = Smarty_Internal_Configfileparser::TPC_NAKED_STRING; - $this->yypopstate(); + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + $this->yypopstate(); } + + public function yylex4() { if (!isset($this->yy_global_pattern4)) { @@ -530,23 +490,21 @@ class Smarty_Internal_Configfilelexer if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern4, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern4,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr( - $this->data, - $this->counter, - 5 - ) . '... state COMMENT'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state COMMENT'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -564,36 +522,41 @@ class Smarty_Internal_Configfilelexer } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { + } } else { throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[ $this->counter ]); + ': ' . $this->data[$this->counter]); } break; } while (true); - } - public function yy_r4_1() - { - return false; - } - - public function yy_r4_2() - { - $this->token = Smarty_Internal_Configfileparser::TPC_NAKED_STRING; } // end function + + const COMMENT = 4; + public function yy_r4_1() + { + + return false; + } + public function yy_r4_2() + { + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + } public function yy_r4_3() { - $this->token = Smarty_Internal_Configfileparser::TPC_NEWLINE; - $this->yypopstate(); + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NEWLINE; + $this->yypopstate(); } + + public function yylex5() { if (!isset($this->yy_global_pattern5)) { @@ -602,23 +565,21 @@ class Smarty_Internal_Configfilelexer if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern5, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern5,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr( - $this->data, - $this->counter, - 5 - ) . '... state SECTION'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state SECTION'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -636,30 +597,34 @@ class Smarty_Internal_Configfilelexer } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { + } } else { throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[ $this->counter ]); + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const SECTION = 5; public function yy_r5_1() { - $this->token = Smarty_Internal_Configfileparser::TPC_DOT; - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_DOT; + } public function yy_r5_2() { - $this->token = Smarty_Internal_Configfileparser::TPC_SECTION; - $this->yypopstate(); - } // end function + + $this->token = \Smarty\Parser\ConfigfileParser::TPC_SECTION; + $this->yypopstate(); + } + public function yylex6() { @@ -669,23 +634,21 @@ class Smarty_Internal_Configfilelexer if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); } - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } + do { - if (preg_match($this->yy_global_pattern6, $this->data, $yymatches, 0, $this->counter)) { - if (!isset($yymatches[ 0 ][ 1 ])) { - $yymatches = preg_grep("/(.|\s)+/", $yymatches); + if (preg_match($this->yy_global_pattern6,$this->data, $yymatches, 0, $this->counter)) { + if (!isset($yymatches[ 0 ][1])) { + $yymatches = preg_grep("/(.|\s)+/", $yymatches); } else { $yymatches = array_filter($yymatches); } if (empty($yymatches)) { throw new Exception('Error: lexing failed because a rule matched' . - ' an empty string. Input "' . substr( - $this->data, - $this->counter, - 5 - ) . '... state TRIPPLE'); + ' an empty string. Input "' . substr($this->data, + $this->counter, 5) . '... state TRIPPLE'); } next($yymatches); // skip global match $this->token = key($yymatches); // token number @@ -703,37 +666,42 @@ class Smarty_Internal_Configfilelexer } elseif ($r === false) { $this->counter += strlen($this->value); $this->line += substr_count($this->value, "\n"); - if ($this->counter >= $this->dataLength) { + if ($this->counter >= $this->dataLength) { return false; // end of input } // skip this token continue; - } - } else { + } } else { throw new Exception('Unexpected input at line' . $this->line . - ': ' . $this->data[ $this->counter ]); + ': ' . $this->data[$this->counter]); } break; } while (true); - } + } // end function + + + const TRIPPLE = 6; public function yy_r6_1() { - $this->token = Smarty_Internal_Configfileparser::TPC_TRIPPLE_QUOTES_END; - $this->yypopstate(); - $this->yypushstate(self::START); - } + $this->token = \Smarty\Parser\ConfigfileParser::TPC_TRIPPLE_QUOTES_END; + $this->yypopstate(); + $this->yypushstate(self::START); + } public function yy_r6_2() { - $to = strlen($this->data); - preg_match("/\"\"\"[ \t\r]*[\n#;]/", $this->data, $match, PREG_OFFSET_CAPTURE, $this->counter); - if (isset($match[ 0 ][ 1 ])) { - $to = $match[ 0 ][ 1 ]; - } else { - $this->compiler->trigger_config_file_error('missing or misspelled literal closing tag'); - } - $this->value = substr($this->data, $this->counter, $to - $this->counter); - $this->token = Smarty_Internal_Configfileparser::TPC_TRIPPLE_TEXT; + + $to = strlen($this->data); + preg_match("/\"\"\"[ \t\r]*[\n#;]/",$this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1]; + } else { + $this->compiler->trigger_config_file_error ('missing or misspelled literal closing tag'); + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = \Smarty\Parser\ConfigfileParser::TPC_TRIPPLE_TEXT; } + + } diff --git a/src/Lexer/ConfigfileLexer.plex b/src/Lexer/ConfigfileLexer.plex new file mode 100644 index 0000000..a895050 --- /dev/null +++ b/src/Lexer/ConfigfileLexer.plex @@ -0,0 +1,321 @@ + 'START', 2 => 'VALUE', 3 => 'NAKED_STRING_VALUE', 4 => 'COMMENT', 5 => 'SECTION', 6 => 'TRIPPLE'); + + /** + * storage for assembled token patterns + * + * @var string + */ + private $yy_global_pattern1 = null; + private $yy_global_pattern2 = null; + private $yy_global_pattern3 = null; + private $yy_global_pattern4 = null; + private $yy_global_pattern5 = null; + private $yy_global_pattern6 = null; + + /** + * token names + * + * @var array + */ + public $smarty_token_names = array( // Text for parser error messages + ); + + /** + * constructor + * + * @param string $data template source + * @param \Smarty\Compiler\Configfile $compiler + */ + public function __construct($data, \Smarty\Compiler\Configfile $compiler) + { + $this->data = $data . "\n"; //now all lines are \n-terminated + $this->dataLength = strlen($data); + $this->counter = 0; + if (preg_match('/^\xEF\xBB\xBF/', $this->data, $match)) { + $this->counter += strlen($match[0]); + } + $this->line = 1; + $this->compiler = $compiler; + $this->smarty = $compiler->smarty; + $this->configBooleanize = $this->smarty->config_booleanize; + } + + public function replace ($input) { + return $input; + } + + public function PrintTrace() + { + $this->yyTraceFILE = fopen('php://output', 'w'); + $this->yyTracePrompt = '
'; + } + + +/*!lex2php +%input $this->data +%counter $this->counter +%token $this->token +%value $this->value +%line $this->line +commentstart = /#|;/ +openB = /\[/ +closeB = /\]/ +section = /.*?(?=[\.=\[\]\r\n])/ +equal = /=/ +whitespace = /[ \t\r]+/ +dot = /\./ +id = /[0-9]*[a-zA-Z_]\w*/ +newline = /\n/ +single_quoted_string = /'[^'\\]*(?:\\.[^'\\]*)*'(?=[ \t\r]*[\n#;])/ +double_quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"(?=[ \t\r]*[\n#;])/ +tripple_quotes = /"""/ +tripple_quotes_end = /"""(?=[ \t\r]*[\n#;])/ +text = /[\S\s]/ +float = /\d+\.\d+(?=[ \t\r]*[\n#;])/ +int = /\d+(?=[ \t\r]*[\n#;])/ +maybe_bool = /[a-zA-Z]+(?=[ \t\r]*[\n#;])/ +naked_string = /[^\n]+?(?=[ \t\r]*\n)/ +*/ + +/*!lex2php +%statename START + +commentstart { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_COMMENTSTART; + $this->yypushstate(self::COMMENT); +} +openB { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_OPENB; + $this->yypushstate(self::SECTION); +} +closeB { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_CLOSEB; +} +equal { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_EQUAL; + $this->yypushstate(self::VALUE); +} +whitespace { + return false; +} +newline { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NEWLINE; +} +id { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_ID; +} +text { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_OTHER; +} + +*/ + +/*!lex2php +%statename VALUE + +whitespace { + return false; +} +float { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_FLOAT; + $this->yypopstate(); +} +int { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_INT; + $this->yypopstate(); +} +tripple_quotes { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_TRIPPLE_QUOTES; + $this->yypushstate(self::TRIPPLE); +} +single_quoted_string { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_SINGLE_QUOTED_STRING; + $this->yypopstate(); +} +double_quoted_string { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_DOUBLE_QUOTED_STRING; + $this->yypopstate(); +} +maybe_bool { + if (!$this->configBooleanize || !in_array(strtolower($this->value), array('true', 'false', 'on', 'off', 'yes', 'no')) ) { + $this->yypopstate(); + $this->yypushstate(self::NAKED_STRING_VALUE); + return true; //reprocess in new state + } else { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_BOOL; + $this->yypopstate(); + } +} +naked_string { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + $this->yypopstate(); +} +newline { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + $this->value = ''; + $this->yypopstate(); +} + +*/ + +/*!lex2php +%statename NAKED_STRING_VALUE + +naked_string { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; + $this->yypopstate(); +} + +*/ + +/*!lex2php +%statename COMMENT + +whitespace { + return false; +} +naked_string { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NAKED_STRING; +} +newline { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_NEWLINE; + $this->yypopstate(); +} + +*/ + +/*!lex2php +%statename SECTION + +dot { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_DOT; +} +section { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_SECTION; + $this->yypopstate(); +} + +*/ +/*!lex2php +%statename TRIPPLE + +tripple_quotes_end { + $this->token = \Smarty\Parser\ConfigfileParser::TPC_TRIPPLE_QUOTES_END; + $this->yypopstate(); + $this->yypushstate(self::START); +} +text { + $to = strlen($this->data); + preg_match("/\"\"\"[ \t\r]*[\n#;]/",$this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1]; + } else { + $this->compiler->trigger_config_file_error ('missing or misspelled literal closing tag'); + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = \Smarty\Parser\ConfigfileParser::TPC_TRIPPLE_TEXT; +} +*/ + +} diff --git a/src/sysplugins/smarty_internal_templatelexer.php b/src/Lexer/TemplateLexer.php similarity index 79% rename from src/sysplugins/smarty_internal_templatelexer.php rename to src/Lexer/TemplateLexer.php index 5ca4820..2e7f330 100644 --- a/src/sysplugins/smarty_internal_templatelexer.php +++ b/src/Lexer/TemplateLexer.php @@ -1,4 +1,7 @@ */ -class Smarty_Internal_Templatelexer +class TemplateLexer { /** * Source @@ -67,13 +70,6 @@ class Smarty_Internal_Templatelexer */ public $taglineno; - /** - * php code type - * - * @var string - */ - public $phpType = ''; - /** * state number * @@ -91,7 +87,7 @@ class Smarty_Internal_Templatelexer /** * compiler object * - * @var Smarty_Internal_TemplateCompilerBase + * @var \Smarty\Compiler\Template */ public $compiler = null; @@ -161,7 +157,6 @@ class Smarty_Internal_Templatelexer 'COMMENT' => 'comment', 'AS' => 'as', 'TO' => 'to', - 'PHP' => '" '"<", "==" ... logical operator', 'TLOGOP' => '"lt", "eq" ... logical operator; "is div by" ... if condition', 'SCOND' => '"is even" ... if condition', @@ -227,9 +222,9 @@ class Smarty_Internal_Templatelexer * constructor * * @param string $source template source - * @param Smarty_Internal_TemplateCompilerBase $compiler + * @param \Smarty\Compiler\Template $compiler */ - public function __construct($source, Smarty_Internal_TemplateCompilerBase $compiler) + public function __construct($source, \Smarty\Compiler\Template $compiler) { $this->data = $source; $this->dataLength = strlen($this->data); @@ -238,7 +233,7 @@ class Smarty_Internal_Templatelexer $this->counter += strlen($match[0]); } $this->line = 1; - $this->smarty = $compiler->template->smarty; + $this->smarty = $compiler->getTemplate()->getSmarty(); $this->compiler = $compiler; $this->compiler->initDelimiterPreg(); $this->smarty_token_names['LDEL'] = $this->smarty->getLeftDelimiter(); @@ -380,7 +375,7 @@ class Smarty_Internal_Templatelexer public function yy_r1_1() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r1_2() { @@ -398,18 +393,18 @@ class Smarty_Internal_Templatelexer public function yy_r1_4() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r1_6() { - $this->token = Smarty_Internal_Templateparser::TP_LITERALSTART; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERALSTART; $this->yypushstate(self::LITERAL); } public function yy_r1_8() { - $this->token = Smarty_Internal_Templateparser::TP_LITERALEND; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERALEND; $this->yypushstate(self::LITERAL); } public function yy_r1_10() @@ -430,14 +425,14 @@ class Smarty_Internal_Templatelexer $to = $match[0][1]; } $this->value = substr($this->data,$this->counter,$to-$this->counter); - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yylex2() { if (!isset($this->yy_global_pattern2)) { - $this->yy_global_pattern2 = $this->replace("/\G((SMARTYldel)SMARTYal(if|elseif|else if|while)\\s+)|\G((SMARTYldel)SMARTYalfor\\s+)|\G((SMARTYldel)SMARTYalforeach(?![^\s]))|\G((SMARTYldel)SMARTYalsetfilter\\s+)|\G((SMARTYldel)SMARTYalmake_nocache\\s+)|\G((SMARTYldel)SMARTYal[0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$]smarty\\.block\\.(child|parent)\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/][0-9]*[a-zA-Z_]\\w*\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$][0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/])|\G((SMARTYldel)SMARTYal)/isS"); + $this->yy_global_pattern2 = $this->replace("/\G((SMARTYldel)SMARTYal(if|elseif|else if|while)\\s+)|\G((SMARTYldel)SMARTYalfor\\s+)|\G((SMARTYldel)SMARTYalforeach(?![^\s]))|\G((SMARTYldel)SMARTYalsetfilter\\s+)|\G((SMARTYldel)SMARTYal[0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$]smarty\\.block\\.(child|parent)\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/][0-9]*[a-zA-Z_]\\w*\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[$][0-9]*[a-zA-Z_]\\w*(\\s+nocache)?\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal[\/])|\G((SMARTYldel)SMARTYal)/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); @@ -493,84 +488,77 @@ class Smarty_Internal_Templatelexer public function yy_r2_1() { - $this->token = Smarty_Internal_Templateparser::TP_LDELIF; + $this->token = \Smarty\Parser\TemplateParser::TP_LDELIF; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } public function yy_r2_4() { - $this->token = Smarty_Internal_Templateparser::TP_LDELFOR; + $this->token = \Smarty\Parser\TemplateParser::TP_LDELFOR; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } public function yy_r2_6() { - $this->token = Smarty_Internal_Templateparser::TP_LDELFOREACH; + $this->token = \Smarty\Parser\TemplateParser::TP_LDELFOREACH; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } public function yy_r2_8() { - $this->token = Smarty_Internal_Templateparser::TP_LDELSETFILTER; + $this->token = \Smarty\Parser\TemplateParser::TP_LDELSETFILTER; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } public function yy_r2_10() - { - - $this->token = Smarty_Internal_Templateparser::TP_LDELMAKENOCACHE; - $this->yybegin(self::TAGBODY); - $this->taglineno = $this->line; - } - public function yy_r2_12() { $this->yypopstate(); - $this->token = Smarty_Internal_Templateparser::TP_SIMPLETAG; + $this->token = \Smarty\Parser\TemplateParser::TP_SIMPLETAG; $this->taglineno = $this->line; } - public function yy_r2_15() + public function yy_r2_13() { $this->yypopstate(); - $this->token = Smarty_Internal_Templateparser::TP_SMARTYBLOCKCHILDPARENT; + $this->token = \Smarty\Parser\TemplateParser::TP_SMARTYBLOCKCHILDPARENT; $this->taglineno = $this->line; } + public function yy_r2_16() + { + + $this->yypopstate(); + $this->token = \Smarty\Parser\TemplateParser::TP_CLOSETAG; + $this->taglineno = $this->line; + } public function yy_r2_18() - { - - $this->yypopstate(); - $this->token = Smarty_Internal_Templateparser::TP_CLOSETAG; - $this->taglineno = $this->line; - } - public function yy_r2_20() { if ($this->_yy_stack[count($this->_yy_stack)-1] === self::TEXT) { $this->yypopstate(); - $this->token = Smarty_Internal_Templateparser::TP_SIMPELOUTPUT; + $this->token = \Smarty\Parser\TemplateParser::TP_SIMPELOUTPUT; $this->taglineno = $this->line; } else { $this->value = $this->smarty->getLeftDelimiter(); - $this->token = Smarty_Internal_Templateparser::TP_LDEL; + $this->token = \Smarty\Parser\TemplateParser::TP_LDEL; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } } - public function yy_r2_23() + public function yy_r2_21() { - $this->token = Smarty_Internal_Templateparser::TP_LDELSLASH; + $this->token = \Smarty\Parser\TemplateParser::TP_LDELSLASH; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } - public function yy_r2_25() + public function yy_r2_23() { - $this->token = Smarty_Internal_Templateparser::TP_LDEL; + $this->token = \Smarty\Parser\TemplateParser::TP_LDEL; $this->yybegin(self::TAGBODY); $this->taglineno = $this->line; } @@ -579,7 +567,7 @@ class Smarty_Internal_Templatelexer public function yylex3() { if (!isset($this->yy_global_pattern3)) { - $this->yy_global_pattern3 = $this->replace("/\G(\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(\\s+is\\s+in\\s+)|\G(\\s+as\\s+)|\G(\\s+to\\s+)|\G(\\s+step\\s+)|\G(\\s+instanceof\\s+)|\G(\\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\\s*)|\G(\\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even|div)\\s+by\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even))|\G([!]\\s*|not\\s+)|\G([(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\\s*)|\G(\\s*[(]\\s*)|\G(\\s*[)])|\G(\\[\\s*)|\G(\\s*\\])|\G(\\s*[-][>]\\s*)|\G(\\s*[=][>]\\s*)|\G(\\s*[=]\\s*)|\G(([+]|[-]){2})|\G(\\s*([+]|[-])\\s*)|\G(\\s*([*]{1,2}|[%\/^&]|[<>]{2})\\s*)|\G([@])|\G(array\\s*[(]\\s*)|\G([#])|\G(\\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\\s*[=]\\s*)|\G(([0-9]*[a-zA-Z_]\\w*)?(\\\\[0-9]*[a-zA-Z_]\\w*)+)|\G([0-9]*[a-zA-Z_]\\w*)|\G(\\d+)|\G([`])|\G([|][@]?)|\G([.])|\G(\\s*[,]\\s*)|\G(\\s*[;]\\s*)|\G([:]{2})|\G(\\s*[:]\\s*)|\G(\\s*[?]\\s*)|\G(0[xX][0-9a-fA-F]+)|\G(\\s+)|\G([\S\s])/isS"); + $this->yy_global_pattern3 = $this->replace("/\G(\\s*SMARTYrdel)|\G((SMARTYldel)SMARTYal)|\G([\"])|\G('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')|\G([$][0-9]*[a-zA-Z_]\\w*)|\G([$])|\G(\\s+is\\s+(not\\s+)?in\\s+)|\G(\\s+as\\s+)|\G(\\s+to\\s+)|\G(\\s+step\\s+)|\G(\\s+instanceof\\s+)|\G(\\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\\s*)|\G(\\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even|div)\\s+by\\s+)|\G(\\s+is\\s+(not\\s+)?(odd|even))|\G([!]\\s*|not\\s+)|\G([(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\\s*)|\G(\\s*[(]\\s*)|\G(\\s*[)])|\G(\\[\\s*)|\G(\\s*\\])|\G(\\s*[-][>]\\s*)|\G(\\s*[=][>]\\s*)|\G(\\s*[=]\\s*)|\G(([+]|[-]){2})|\G(\\s*([+]|[-])\\s*)|\G(\\s*([*]{1,2}|[%\/^&]|[<>]{2})\\s*)|\G([@])|\G(array\\s*[(]\\s*)|\G([#])|\G(\\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\\s*[=]\\s*)|\G(([0-9]*[a-zA-Z_]\\w*)?(\\\\[0-9]*[a-zA-Z_]\\w*)+)|\G([0-9]*[a-zA-Z_]\\w*)|\G(\\d+)|\G([`])|\G([|][@]?)|\G([.])|\G(\\s*[,]\\s*)|\G(\\s*[;]\\s*)|\G([:]{2})|\G(\\s*[:]\\s*)|\G(\\s*[?]\\s*)|\G(0[xX][0-9a-fA-F]+)|\G(\\s+)|\G([\S\s])/isS"); } if (!isset($this->dataLength)) { $this->dataLength = strlen($this->data); @@ -635,7 +623,7 @@ class Smarty_Internal_Templatelexer public function yy_r3_1() { - $this->token = Smarty_Internal_Templateparser::TP_RDEL; + $this->token = \Smarty\Parser\TemplateParser::TP_RDEL; $this->yypopstate(); } public function yy_r3_2() @@ -647,227 +635,227 @@ class Smarty_Internal_Templatelexer public function yy_r3_4() { - $this->token = Smarty_Internal_Templateparser::TP_QUOTE; + $this->token = \Smarty\Parser\TemplateParser::TP_QUOTE; $this->yypushstate(self::DOUBLEQUOTEDSTRING); $this->compiler->enterDoubleQuote(); } public function yy_r3_5() { - $this->token = Smarty_Internal_Templateparser::TP_SINGLEQUOTESTRING; + $this->token = \Smarty\Parser\TemplateParser::TP_SINGLEQUOTESTRING; } public function yy_r3_6() { - $this->token = Smarty_Internal_Templateparser::TP_DOLLARID; + $this->token = \Smarty\Parser\TemplateParser::TP_DOLLARID; } public function yy_r3_7() { - $this->token = Smarty_Internal_Templateparser::TP_DOLLAR; + $this->token = \Smarty\Parser\TemplateParser::TP_DOLLAR; } public function yy_r3_8() { - $this->token = Smarty_Internal_Templateparser::TP_ISIN; - } - public function yy_r3_9() - { - - $this->token = Smarty_Internal_Templateparser::TP_AS; + $this->token = \Smarty\Parser\TemplateParser::TP_ISIN; } public function yy_r3_10() { - $this->token = Smarty_Internal_Templateparser::TP_TO; + $this->token = \Smarty\Parser\TemplateParser::TP_AS; } public function yy_r3_11() { - $this->token = Smarty_Internal_Templateparser::TP_STEP; + $this->token = \Smarty\Parser\TemplateParser::TP_TO; } public function yy_r3_12() { - $this->token = Smarty_Internal_Templateparser::TP_INSTANCEOF; + $this->token = \Smarty\Parser\TemplateParser::TP_STEP; } public function yy_r3_13() { - $this->token = Smarty_Internal_Templateparser::TP_LOGOP; + $this->token = \Smarty\Parser\TemplateParser::TP_INSTANCEOF; } - public function yy_r3_15() + public function yy_r3_14() { - $this->token = Smarty_Internal_Templateparser::TP_SLOGOP; + $this->token = \Smarty\Parser\TemplateParser::TP_LOGOP; } - public function yy_r3_17() + public function yy_r3_16() { - $this->token = Smarty_Internal_Templateparser::TP_TLOGOP; + $this->token = \Smarty\Parser\TemplateParser::TP_SLOGOP; } - public function yy_r3_20() + public function yy_r3_18() { - $this->token = Smarty_Internal_Templateparser::TP_SINGLECOND; + $this->token = \Smarty\Parser\TemplateParser::TP_TLOGOP; } - public function yy_r3_23() + public function yy_r3_21() { - $this->token = Smarty_Internal_Templateparser::TP_NOT; + $this->token = \Smarty\Parser\TemplateParser::TP_SINGLECOND; } public function yy_r3_24() { - $this->token = Smarty_Internal_Templateparser::TP_TYPECAST; + $this->token = \Smarty\Parser\TemplateParser::TP_NOT; } - public function yy_r3_28() + public function yy_r3_25() { - $this->token = Smarty_Internal_Templateparser::TP_OPENP; + $this->token = \Smarty\Parser\TemplateParser::TP_TYPECAST; } public function yy_r3_29() { - $this->token = Smarty_Internal_Templateparser::TP_CLOSEP; + $this->token = \Smarty\Parser\TemplateParser::TP_OPENP; } public function yy_r3_30() { - $this->token = Smarty_Internal_Templateparser::TP_OPENB; + $this->token = \Smarty\Parser\TemplateParser::TP_CLOSEP; } public function yy_r3_31() { - $this->token = Smarty_Internal_Templateparser::TP_CLOSEB; + $this->token = \Smarty\Parser\TemplateParser::TP_OPENB; } public function yy_r3_32() { - $this->token = Smarty_Internal_Templateparser::TP_PTR; + $this->token = \Smarty\Parser\TemplateParser::TP_CLOSEB; } public function yy_r3_33() { - $this->token = Smarty_Internal_Templateparser::TP_APTR; + $this->token = \Smarty\Parser\TemplateParser::TP_PTR; } public function yy_r3_34() { - $this->token = Smarty_Internal_Templateparser::TP_EQUAL; + $this->token = \Smarty\Parser\TemplateParser::TP_APTR; } public function yy_r3_35() { - $this->token = Smarty_Internal_Templateparser::TP_INCDEC; + $this->token = \Smarty\Parser\TemplateParser::TP_EQUAL; } - public function yy_r3_37() + public function yy_r3_36() { - $this->token = Smarty_Internal_Templateparser::TP_UNIMATH; + $this->token = \Smarty\Parser\TemplateParser::TP_INCDEC; } - public function yy_r3_39() + public function yy_r3_38() { - $this->token = Smarty_Internal_Templateparser::TP_MATH; + $this->token = \Smarty\Parser\TemplateParser::TP_UNIMATH; } - public function yy_r3_41() + public function yy_r3_40() { - $this->token = Smarty_Internal_Templateparser::TP_AT; + $this->token = \Smarty\Parser\TemplateParser::TP_MATH; } public function yy_r3_42() { - $this->token = Smarty_Internal_Templateparser::TP_ARRAYOPEN; + $this->token = \Smarty\Parser\TemplateParser::TP_AT; } public function yy_r3_43() { - $this->token = Smarty_Internal_Templateparser::TP_HATCH; + $this->token = \Smarty\Parser\TemplateParser::TP_ARRAYOPEN; } public function yy_r3_44() + { + + $this->token = \Smarty\Parser\TemplateParser::TP_HATCH; + } + public function yy_r3_45() { // resolve conflicts with shorttag and right_delimiter starting with '=' if (substr($this->data, $this->counter + strlen($this->value) - 1, $this->compiler->getRdelLength()) === $this->smarty->getRightDelimiter()) { preg_match('/\s+/',$this->value,$match); $this->value = $match[0]; - $this->token = Smarty_Internal_Templateparser::TP_SPACE; + $this->token = \Smarty\Parser\TemplateParser::TP_SPACE; } else { - $this->token = Smarty_Internal_Templateparser::TP_ATTR; + $this->token = \Smarty\Parser\TemplateParser::TP_ATTR; } } - public function yy_r3_45() + public function yy_r3_46() { - $this->token = Smarty_Internal_Templateparser::TP_NAMESPACE; - } - public function yy_r3_48() - { - - $this->token = Smarty_Internal_Templateparser::TP_ID; + $this->token = \Smarty\Parser\TemplateParser::TP_NAMESPACE; } public function yy_r3_49() { - $this->token = Smarty_Internal_Templateparser::TP_INTEGER; + $this->token = \Smarty\Parser\TemplateParser::TP_ID; } public function yy_r3_50() { - $this->token = Smarty_Internal_Templateparser::TP_BACKTICK; - $this->yypopstate(); + $this->token = \Smarty\Parser\TemplateParser::TP_INTEGER; } public function yy_r3_51() { - $this->token = Smarty_Internal_Templateparser::TP_VERT; + $this->token = \Smarty\Parser\TemplateParser::TP_BACKTICK; + $this->yypopstate(); } public function yy_r3_52() { - $this->token = Smarty_Internal_Templateparser::TP_DOT; + $this->token = \Smarty\Parser\TemplateParser::TP_VERT; } public function yy_r3_53() { - $this->token = Smarty_Internal_Templateparser::TP_COMMA; + $this->token = \Smarty\Parser\TemplateParser::TP_DOT; } public function yy_r3_54() { - $this->token = Smarty_Internal_Templateparser::TP_SEMICOLON; + $this->token = \Smarty\Parser\TemplateParser::TP_COMMA; } public function yy_r3_55() { - $this->token = Smarty_Internal_Templateparser::TP_DOUBLECOLON; + $this->token = \Smarty\Parser\TemplateParser::TP_SEMICOLON; } public function yy_r3_56() { - $this->token = Smarty_Internal_Templateparser::TP_COLON; + $this->token = \Smarty\Parser\TemplateParser::TP_DOUBLECOLON; } public function yy_r3_57() { - $this->token = Smarty_Internal_Templateparser::TP_QMARK; + $this->token = \Smarty\Parser\TemplateParser::TP_COLON; } public function yy_r3_58() { - $this->token = Smarty_Internal_Templateparser::TP_HEX; + $this->token = \Smarty\Parser\TemplateParser::TP_QMARK; } public function yy_r3_59() { - $this->token = Smarty_Internal_Templateparser::TP_SPACE; + $this->token = \Smarty\Parser\TemplateParser::TP_HEX; } public function yy_r3_60() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_SPACE; + } + public function yy_r3_61() + { + + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } @@ -932,16 +920,16 @@ class Smarty_Internal_Templatelexer { $this->literal_cnt++; - $this->token = Smarty_Internal_Templateparser::TP_LITERAL; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERAL; } public function yy_r4_3() { if ($this->literal_cnt) { $this->literal_cnt--; - $this->token = Smarty_Internal_Templateparser::TP_LITERAL; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERAL; } else { - $this->token = Smarty_Internal_Templateparser::TP_LITERALEND; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERALEND; $this->yypopstate(); } } @@ -959,7 +947,7 @@ class Smarty_Internal_Templatelexer $this->compiler->trigger_template_error ("missing or misspelled literal closing tag"); } $this->value = substr($this->data,$this->counter,$to-$this->counter); - $this->token = Smarty_Internal_Templateparser::TP_LITERAL; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERAL; } @@ -1022,17 +1010,17 @@ class Smarty_Internal_Templatelexer public function yy_r5_1() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r5_3() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r5_5() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r5_7() { @@ -1049,20 +1037,20 @@ class Smarty_Internal_Templatelexer public function yy_r5_11() { - $this->token = Smarty_Internal_Templateparser::TP_LDEL; + $this->token = \Smarty\Parser\TemplateParser::TP_LDEL; $this->taglineno = $this->line; $this->yypushstate(self::TAGBODY); } public function yy_r5_13() { - $this->token = Smarty_Internal_Templateparser::TP_QUOTE; + $this->token = \Smarty\Parser\TemplateParser::TP_QUOTE; $this->yypopstate(); } public function yy_r5_14() { - $this->token = Smarty_Internal_Templateparser::TP_BACKTICK; + $this->token = \Smarty\Parser\TemplateParser::TP_BACKTICK; $this->value = substr($this->value,0,-1); $this->yypushstate(self::TAGBODY); $this->taglineno = $this->line; @@ -1070,24 +1058,24 @@ class Smarty_Internal_Templatelexer public function yy_r5_15() { - $this->token = Smarty_Internal_Templateparser::TP_DOLLARID; + $this->token = \Smarty\Parser\TemplateParser::TP_DOLLARID; } public function yy_r5_16() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r5_17() { - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } public function yy_r5_22() { $to = $this->dataLength; $this->value = substr($this->data,$this->counter,$to-$this->counter); - $this->token = Smarty_Internal_Templateparser::TP_TEXT; + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; } } diff --git a/src/Lexer/TemplateLexer.plex b/src/Lexer/TemplateLexer.plex new file mode 100644 index 0000000..282a99c --- /dev/null +++ b/src/Lexer/TemplateLexer.plex @@ -0,0 +1,677 @@ + + */ +class TemplateLexer +{ + /** + * Source + * + * @var string + */ + public $data; + + /** + * Source length + * + * @var int + */ + public $dataLength = null; + + /** + * byte counter + * + * @var int + */ + public $counter; + + /** + * token number + * + * @var int + */ + public $token; + + /** + * token value + * + * @var string + */ + public $value; + + /** + * current line + * + * @var int + */ + public $line; + + /** + * tag start line + * + * @var + */ + public $taglineno; + + /** + * state number + * + * @var int + */ + public $state = 1; + + /** + * Smarty object + * + * @var Smarty + */ + public $smarty = null; + + /** + * compiler object + * + * @var \Smarty\Compiler\Template + */ + public $compiler = null; + + /** + * trace file + * + * @var resource + */ + public $yyTraceFILE; + + /** + * trace prompt + * + * @var string + */ + public $yyTracePrompt; + + /** + * XML flag true while processing xml + * + * @var bool + */ + public $is_xml = false; + + /** + * state names + * + * @var array + */ + public $state_name = array(1 => 'TEXT', 2 => 'TAG', 3 => 'TAGBODY', 4 => 'LITERAL', 5 => 'DOUBLEQUOTEDSTRING',); + + /** + * token names + * + * @var array + */ + public $smarty_token_names = array( // Text for parser error messages + 'NOT' => '(!,not)', + 'OPENP' => '(', + 'CLOSEP' => ')', + 'OPENB' => '[', + 'CLOSEB' => ']', + 'PTR' => '->', + 'APTR' => '=>', + 'EQUAL' => '=', + 'NUMBER' => 'number', + 'UNIMATH' => '+" , "-', + 'MATH' => '*" , "/" , "%', + 'INCDEC' => '++" , "--', + 'SPACE' => ' ', + 'DOLLAR' => '$', + 'SEMICOLON' => ';', + 'COLON' => ':', + 'DOUBLECOLON' => '::', + 'AT' => '@', + 'HATCH' => '#', + 'QUOTE' => '"', + 'BACKTICK' => '`', + 'VERT' => '"|" modifier', + 'DOT' => '.', + 'COMMA' => '","', + 'QMARK' => '"?"', + 'ID' => 'id, name', + 'TEXT' => 'text', + 'LDELSLASH' => '{/..} closing tag', + 'LDEL' => '{...} Smarty tag', + 'COMMENT' => 'comment', + 'AS' => 'as', + 'TO' => 'to', + 'LOGOP' => '"<", "==" ... logical operator', + 'TLOGOP' => '"lt", "eq" ... logical operator; "is div by" ... if condition', + 'SCOND' => '"is even" ... if condition', + ); + + /** + * literal tag nesting level + * + * @var int + */ + private $literal_cnt = 0; + + /** + * preg token pattern for state TEXT + * + * @var string + */ + private $yy_global_pattern1 = null; + + /** + * preg token pattern for state TAG + * + * @var string + */ + private $yy_global_pattern2 = null; + + /** + * preg token pattern for state TAGBODY + * + * @var string + */ + private $yy_global_pattern3 = null; + + /** + * preg token pattern for state LITERAL + * + * @var string + */ + private $yy_global_pattern4 = null; + + /** + * preg token pattern for state DOUBLEQUOTEDSTRING + * + * @var null + */ + private $yy_global_pattern5 = null; + + /** + * preg token pattern for text + * + * @var null + */ + private $yy_global_text = null; + + /** + * preg token pattern for literal + * + * @var null + */ + private $yy_global_literal = null; + + /** + * constructor + * + * @param string $source template source + * @param \Smarty\Compiler\Template $compiler + */ + public function __construct($source, \Smarty\Compiler\Template $compiler) + { + $this->data = $source; + $this->dataLength = strlen($this->data); + $this->counter = 0; + if (preg_match('/^\xEF\xBB\xBF/i', $this->data, $match)) { + $this->counter += strlen($match[0]); + } + $this->line = 1; + $this->smarty = $compiler->getTemplate()->getSmarty(); + $this->compiler = $compiler; + $this->compiler->initDelimiterPreg(); + $this->smarty_token_names['LDEL'] = $this->smarty->getLeftDelimiter(); + $this->smarty_token_names['RDEL'] = $this->smarty->getRightDelimiter(); + } + + /** + * open lexer/parser trace file + * + */ + public function PrintTrace() + { + $this->yyTraceFILE = fopen('php://output', 'w'); + $this->yyTracePrompt = '
'; + } + + /** + * replace placeholders with runtime preg code + * + * @param string $preg + * + * @return string + */ + public function replace($preg) + { + return $this->compiler->replaceDelimiter($preg); + } + + /** + * check if current value is an autoliteral left delimiter + * + * @return bool + */ + public function isAutoLiteral() + { + return $this->smarty->getAutoLiteral() && isset($this->value[ $this->compiler->getLdelLength() ]) ? + strpos(" \n\t\r", $this->value[ $this->compiler->getLdelLength() ]) !== false : false; + } + + /*!lex2php + %input $this->data + %counter $this->counter + %token $this->token + %value $this->value + %line $this->line + userliteral = ~(SMARTYldel)SMARTYautoliteral\s+SMARTYliteral~ + char = ~[\S\s]~ + textdoublequoted = ~([^"\\]*?)((?:\\.[^"\\]*?)*?)(?=((SMARTYldel)SMARTYal|\$|`\$|"SMARTYliteral))~ + namespace = ~([0-9]*[a-zA-Z_]\w*)?(\\[0-9]*[a-zA-Z_]\w*)+~ + emptyjava = ~[{][}]~ + slash = ~[/]~ + ldel = ~(SMARTYldel)SMARTYal~ + rdel = ~\s*SMARTYrdel~ + nocacherdel = ~(\s+nocache)?\s*SMARTYrdel~ + smartyblockchildparent = ~[\$]smarty\.block\.(child|parent)~ + integer = ~\d+~ + hex = ~0[xX][0-9a-fA-F]+~ + math = ~\s*([*]{1,2}|[%/^&]|[<>]{2})\s*~ + comment = ~(SMARTYldel)SMARTYal[*]~ + incdec = ~([+]|[-]){2}~ + unimath = ~\s*([+]|[-])\s*~ + openP = ~\s*[(]\s*~ + closeP = ~\s*[)]~ + openB = ~\[\s*~ + closeB = ~\s*\]~ + dollar = ~[$]~ + dot = ~[.]~ + comma = ~\s*[,]\s*~ + doublecolon = ~[:]{2}~ + colon = ~\s*[:]\s*~ + at = ~[@]~ + hatch = ~[#]~ + semicolon = ~\s*[;]\s*~ + equal = ~\s*[=]\s*~ + space = ~\s+~ + ptr = ~\s*[-][>]\s*~ + aptr = ~\s*[=][>]\s*~ + singlequotestring = ~'[^'\\]*(?:\\.[^'\\]*)*'~ + backtick = ~[`]~ + vert = ~[|][@]?~ + qmark = ~\s*[?]\s*~ + constant = ~[_]+[A-Z0-9][0-9A-Z_]*|[A-Z][0-9A-Z_]*(?![0-9A-Z_]*[a-z])~ + attr = ~\s+[0-9]*[a-zA-Z_][a-zA-Z0-9_\-:]*\s*[=]\s*~ + id = ~[0-9]*[a-zA-Z_]\w*~ + literal = ~literal~ + strip = ~strip~ + lop = ~\s*([!=][=]{1,2}|[<][=>]?|[>][=]?|[&|]{2})\s*~ + slop = ~\s+(eq|ne|neq|gt|ge|gte|lt|le|lte|mod|and|or|xor)\s+~ + tlop = ~\s+is\s+(not\s+)?(odd|even|div)\s+by\s+~ + scond = ~\s+is\s+(not\s+)?(odd|even)~ + isin = ~\s+is\s+(not\s+)?in\s+~ + as = ~\s+as\s+~ + to = ~\s+to\s+~ + step = ~\s+step\s+~ + if = ~(if|elseif|else if|while)\s+~ + for = ~for\s+~ + array = ~array~ + foreach = ~foreach(?![^\s])~ + setfilter = ~setfilter\s+~ + instanceof = ~\s+instanceof\s+~ + not = ~[!]\s*|not\s+~ + typecast = ~[(](int(eger)?|bool(ean)?|float|double|real|string|binary|array|object)[)]\s*~ + double_quote = ~["]~ + */ + /*!lex2php + %statename TEXT + emptyjava { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + comment { + $to = $this->dataLength; + preg_match("/[*]{$this->compiler->getRdelPreg()}[\n]?/",$this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1] + strlen($match[0][0]); + } else { + $this->compiler->trigger_template_error ("missing or misspelled comment closing tag '{$this->smarty->getRightDelimiter()}'"); + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + return false; + } + userliteral { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + ldel literal rdel { + $this->token = \Smarty\Parser\TemplateParser::TP_LITERALSTART; + $this->yypushstate(self::LITERAL); + } + ldel slash literal rdel { + $this->token = \Smarty\Parser\TemplateParser::TP_LITERALEND; + $this->yypushstate(self::LITERAL); + } + ldel { + $this->yypushstate(self::TAG); + return true; + } + char { + if (!isset($this->yy_global_text)) { + $this->yy_global_text = $this->replace('/(SMARTYldel)SMARTYal/isS'); + } + $to = $this->dataLength; + preg_match($this->yy_global_text, $this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1]; + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + */ + /*!lex2php + %statename TAG + ldel if { + $this->token = \Smarty\Parser\TemplateParser::TP_LDELIF; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + ldel for { + $this->token = \Smarty\Parser\TemplateParser::TP_LDELFOR; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + ldel foreach { + $this->token = \Smarty\Parser\TemplateParser::TP_LDELFOREACH; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + ldel setfilter { + $this->token = \Smarty\Parser\TemplateParser::TP_LDELSETFILTER; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + ldel id nocacherdel { + $this->yypopstate(); + $this->token = \Smarty\Parser\TemplateParser::TP_SIMPLETAG; + $this->taglineno = $this->line; + } + ldel smartyblockchildparent rdel { + $this->yypopstate(); + $this->token = \Smarty\Parser\TemplateParser::TP_SMARTYBLOCKCHILDPARENT; + $this->taglineno = $this->line; + } + ldel slash id rdel { + $this->yypopstate(); + $this->token = \Smarty\Parser\TemplateParser::TP_CLOSETAG; + $this->taglineno = $this->line; + } + ldel dollar id nocacherdel { + if ($this->_yy_stack[count($this->_yy_stack)-1] === self::TEXT) { + $this->yypopstate(); + $this->token = \Smarty\Parser\TemplateParser::TP_SIMPELOUTPUT; + $this->taglineno = $this->line; + } else { + $this->value = $this->smarty->getLeftDelimiter(); + $this->token = \Smarty\Parser\TemplateParser::TP_LDEL; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + } + ldel slash { + $this->token = \Smarty\Parser\TemplateParser::TP_LDELSLASH; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + ldel { + $this->token = \Smarty\Parser\TemplateParser::TP_LDEL; + $this->yybegin(self::TAGBODY); + $this->taglineno = $this->line; + } + */ + /*!lex2php + %statename TAGBODY + rdel { + $this->token = \Smarty\Parser\TemplateParser::TP_RDEL; + $this->yypopstate(); + } + ldel { + $this->yypushstate(self::TAG); + return true; + } + double_quote { + $this->token = \Smarty\Parser\TemplateParser::TP_QUOTE; + $this->yypushstate(self::DOUBLEQUOTEDSTRING); + $this->compiler->enterDoubleQuote(); + } + singlequotestring { + $this->token = \Smarty\Parser\TemplateParser::TP_SINGLEQUOTESTRING; + } + dollar id { + $this->token = \Smarty\Parser\TemplateParser::TP_DOLLARID; + } + dollar { + $this->token = \Smarty\Parser\TemplateParser::TP_DOLLAR; + } + isin { + $this->token = \Smarty\Parser\TemplateParser::TP_ISIN; + } + as { + $this->token = \Smarty\Parser\TemplateParser::TP_AS; + } + to { + $this->token = \Smarty\Parser\TemplateParser::TP_TO; + } + step { + $this->token = \Smarty\Parser\TemplateParser::TP_STEP; + } + instanceof { + $this->token = \Smarty\Parser\TemplateParser::TP_INSTANCEOF; + } + lop { + $this->token = \Smarty\Parser\TemplateParser::TP_LOGOP; + } + slop { + $this->token = \Smarty\Parser\TemplateParser::TP_SLOGOP; + } + tlop { + $this->token = \Smarty\Parser\TemplateParser::TP_TLOGOP; + } + scond { + $this->token = \Smarty\Parser\TemplateParser::TP_SINGLECOND; + } + not{ + $this->token = \Smarty\Parser\TemplateParser::TP_NOT; + } + typecast { + $this->token = \Smarty\Parser\TemplateParser::TP_TYPECAST; + } + openP { + $this->token = \Smarty\Parser\TemplateParser::TP_OPENP; + } + closeP { + $this->token = \Smarty\Parser\TemplateParser::TP_CLOSEP; + } + openB { + $this->token = \Smarty\Parser\TemplateParser::TP_OPENB; + } + closeB { + $this->token = \Smarty\Parser\TemplateParser::TP_CLOSEB; + } + ptr { + $this->token = \Smarty\Parser\TemplateParser::TP_PTR; + } + aptr { + $this->token = \Smarty\Parser\TemplateParser::TP_APTR; + } + equal { + $this->token = \Smarty\Parser\TemplateParser::TP_EQUAL; + } + incdec { + $this->token = \Smarty\Parser\TemplateParser::TP_INCDEC; + } + unimath { + $this->token = \Smarty\Parser\TemplateParser::TP_UNIMATH; + } + math { + $this->token = \Smarty\Parser\TemplateParser::TP_MATH; + } + at { + $this->token = \Smarty\Parser\TemplateParser::TP_AT; + } + array openP { + $this->token = \Smarty\Parser\TemplateParser::TP_ARRAYOPEN; + } + hatch { + $this->token = \Smarty\Parser\TemplateParser::TP_HATCH; + } + attr { + // resolve conflicts with shorttag and right_delimiter starting with '=' + if (substr($this->data, $this->counter + strlen($this->value) - 1, $this->compiler->getRdelLength()) === $this->smarty->getRightDelimiter()) { + preg_match('/\s+/',$this->value,$match); + $this->value = $match[0]; + $this->token = \Smarty\Parser\TemplateParser::TP_SPACE; + } else { + $this->token = \Smarty\Parser\TemplateParser::TP_ATTR; + } + } + namespace { + $this->token = \Smarty\Parser\TemplateParser::TP_NAMESPACE; + } + id { + $this->token = \Smarty\Parser\TemplateParser::TP_ID; + } + integer { + $this->token = \Smarty\Parser\TemplateParser::TP_INTEGER; + } + backtick { + $this->token = \Smarty\Parser\TemplateParser::TP_BACKTICK; + $this->yypopstate(); + } + vert { + $this->token = \Smarty\Parser\TemplateParser::TP_VERT; + } + dot { + $this->token = \Smarty\Parser\TemplateParser::TP_DOT; + } + comma { + $this->token = \Smarty\Parser\TemplateParser::TP_COMMA; + } + semicolon { + $this->token = \Smarty\Parser\TemplateParser::TP_SEMICOLON; + } + doublecolon { + $this->token = \Smarty\Parser\TemplateParser::TP_DOUBLECOLON; + } + colon { + $this->token = \Smarty\Parser\TemplateParser::TP_COLON; + } + qmark { + $this->token = \Smarty\Parser\TemplateParser::TP_QMARK; + } + hex { + $this->token = \Smarty\Parser\TemplateParser::TP_HEX; + } + space { + $this->token = \Smarty\Parser\TemplateParser::TP_SPACE; + } + char { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + */ + + /*!lex2php + %statename LITERAL + ldel literal rdel { + $this->literal_cnt++; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERAL; + } + ldel slash literal rdel { + if ($this->literal_cnt) { + $this->literal_cnt--; + $this->token = \Smarty\Parser\TemplateParser::TP_LITERAL; + } else { + $this->token = \Smarty\Parser\TemplateParser::TP_LITERALEND; + $this->yypopstate(); + } + } + char { + if (!isset($this->yy_global_literal)) { + $this->yy_global_literal = $this->replace('/(SMARTYldel)SMARTYal[\/]?literalSMARTYrdel/isS'); + } + $to = $this->dataLength; + preg_match($this->yy_global_literal, $this->data,$match,PREG_OFFSET_CAPTURE,$this->counter); + if (isset($match[0][1])) { + $to = $match[0][1]; + } else { + $this->compiler->trigger_template_error ("missing or misspelled literal closing tag"); + } + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = \Smarty\Parser\TemplateParser::TP_LITERAL; + } + */ + /*!lex2php + %statename DOUBLEQUOTEDSTRING + userliteral { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + ldel literal rdel { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + ldel slash literal rdel { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + ldel slash { + $this->yypushstate(self::TAG); + return true; + } + ldel id { + $this->yypushstate(self::TAG); + return true; + } + ldel { + $this->token = \Smarty\Parser\TemplateParser::TP_LDEL; + $this->taglineno = $this->line; + $this->yypushstate(self::TAGBODY); + } + double_quote { + $this->token = \Smarty\Parser\TemplateParser::TP_QUOTE; + $this->yypopstate(); + } + backtick dollar { + $this->token = \Smarty\Parser\TemplateParser::TP_BACKTICK; + $this->value = substr($this->value,0,-1); + $this->yypushstate(self::TAGBODY); + $this->taglineno = $this->line; + } + dollar id { + $this->token = \Smarty\Parser\TemplateParser::TP_DOLLARID; + } + dollar { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + textdoublequoted { + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + char { + $to = $this->dataLength; + $this->value = substr($this->data,$this->counter,$to-$this->counter); + $this->token = \Smarty\Parser\TemplateParser::TP_TEXT; + } + */ + } + + \ No newline at end of file diff --git a/src/ParseTree/Base.php b/src/ParseTree/Base.php new file mode 100644 index 0000000..e1140c1 --- /dev/null +++ b/src/ParseTree/Base.php @@ -0,0 +1,45 @@ +data); } diff --git a/src/sysplugins/smarty_internal_parsetree_dq.php b/src/ParseTree/Dq.php similarity index 50% rename from src/sysplugins/smarty_internal_parsetree_dq.php rename to src/ParseTree/Dq.php index 8655f58..b5fca3b 100644 --- a/src/sysplugins/smarty_internal_parsetree_dq.php +++ b/src/ParseTree/Dq.php @@ -1,92 +1,94 @@ subtrees[] = $subtree; - if ($subtree instanceof Smarty_Internal_ParseTree_Tag) { - $parser->block_nesting_level = count($parser->compiler->_tag_stack); + if ($subtree instanceof Tag) { + $parser->block_nesting_level = $parser->compiler->getTagStackCount(); } } /** * Append buffer to subtree * - * @param \Smarty_Internal_Templateparser $parser - * @param Smarty_Internal_ParseTree $subtree parse tree buffer + * @param \Smarty\Parser\TemplateParser $parser + * @param Base $subtree parse tree buffer */ - public function append_subtree(Smarty_Internal_Templateparser $parser, Smarty_Internal_ParseTree $subtree) + public function append_subtree(\Smarty\Parser\TemplateParser $parser, Base $subtree) { $last_subtree = count($this->subtrees) - 1; - if ($last_subtree >= 0 && $this->subtrees[ $last_subtree ] instanceof Smarty_Internal_ParseTree_Tag + if ($last_subtree >= 0 && $this->subtrees[ $last_subtree ] instanceof Tag && $this->subtrees[ $last_subtree ]->saved_block_nesting < $parser->block_nesting_level ) { - if ($subtree instanceof Smarty_Internal_ParseTree_Code) { + if ($subtree instanceof Code) { $this->subtrees[ $last_subtree ]->data = $parser->compiler->appendCode( - $this->subtrees[ $last_subtree ]->data, + (string) $this->subtrees[ $last_subtree ]->data, 'data . ';?>' ); - } elseif ($subtree instanceof Smarty_Internal_ParseTree_DqContent) { + } elseif ($subtree instanceof DqContent) { $this->subtrees[ $last_subtree ]->data = $parser->compiler->appendCode( - $this->subtrees[ $last_subtree ]->data, + (string) $this->subtrees[ $last_subtree ]->data, 'data . '";?>' ); } else { $this->subtrees[ $last_subtree ]->data = - $parser->compiler->appendCode($this->subtrees[ $last_subtree ]->data, $subtree->data); + $parser->compiler->appendCode((string) $this->subtrees[ $last_subtree ]->data, (string) $subtree->data); } } else { $this->subtrees[] = $subtree; } - if ($subtree instanceof Smarty_Internal_ParseTree_Tag) { - $parser->block_nesting_level = count($parser->compiler->_tag_stack); + if ($subtree instanceof Tag) { + $parser->block_nesting_level = $parser->compiler->getTagStackCount(); } } /** * Merge subtree buffer content together * - * @param \Smarty_Internal_Templateparser $parser + * @param \Smarty\Parser\TemplateParser $parser * * @return string compiled template code */ - public function to_smarty_php(Smarty_Internal_Templateparser $parser) + public function to_smarty_php(\Smarty\Parser\TemplateParser $parser) { $code = ''; foreach ($this->subtrees as $subtree) { if ($code !== '') { $code .= '.'; } - if ($subtree instanceof Smarty_Internal_ParseTree_Tag) { + if ($subtree instanceof Tag) { $more_php = $subtree->assign_to_var($parser); } else { $more_php = $subtree->to_smarty_php($parser); } $code .= $more_php; - if (!$subtree instanceof Smarty_Internal_ParseTree_DqContent) { + if (!$subtree instanceof DqContent) { $parser->compiler->has_variable_string = true; } } diff --git a/src/sysplugins/smarty_internal_parsetree_dqcontent.php b/src/ParseTree/DqContent.php similarity index 58% rename from src/sysplugins/smarty_internal_parsetree_dqcontent.php rename to src/ParseTree/DqContent.php index a8ca389..f0a4b06 100644 --- a/src/sysplugins/smarty_internal_parsetree_dqcontent.php +++ b/src/ParseTree/DqContent.php @@ -1,22 +1,24 @@ data . '"'; } diff --git a/src/sysplugins/smarty_internal_parsetree_tag.php b/src/ParseTree/Tag.php similarity index 63% rename from src/sysplugins/smarty_internal_parsetree_tag.php rename to src/ParseTree/Tag.php index e6c7556..05237f2 100644 --- a/src/sysplugins/smarty_internal_parsetree_tag.php +++ b/src/ParseTree/Tag.php @@ -1,10 +1,13 @@ data = $data; $this->saved_block_nesting = $parser->block_nesting_level; @@ -40,11 +43,11 @@ class Smarty_Internal_ParseTree_Tag extends Smarty_Internal_ParseTree /** * Return buffer content * - * @param \Smarty_Internal_Templateparser $parser + * @param \Smarty\Parser\TemplateParser $parser * * @return string content */ - public function to_smarty_php(Smarty_Internal_Templateparser $parser) + public function to_smarty_php(\Smarty\Parser\TemplateParser $parser) { return $this->data; } @@ -52,16 +55,16 @@ class Smarty_Internal_ParseTree_Tag extends Smarty_Internal_ParseTree /** * Return complied code that loads the evaluated output of buffer content into a temporary variable * - * @param \Smarty_Internal_Templateparser $parser + * @param \Smarty\Parser\TemplateParser $parser * * @return string template code */ - public function assign_to_var(Smarty_Internal_Templateparser $parser) + public function assign_to_var(\Smarty\Parser\TemplateParser $parser) { $var = $parser->compiler->getNewPrefixVariable(); - $tmp = $parser->compiler->appendCode('', $this->data); + $tmp = $parser->compiler->appendCode('', (string) $this->data); $tmp = $parser->compiler->appendCode($tmp, ""); - $parser->compiler->prefix_code[] = sprintf('%s', $tmp); + $parser->compiler->appendPrefixCode($tmp); return $var; } } diff --git a/src/sysplugins/smarty_internal_parsetree_template.php b/src/ParseTree/Template.php similarity index 77% rename from src/sysplugins/smarty_internal_parsetree_template.php rename to src/ParseTree/Template.php index 829c420..ce802a0 100644 --- a/src/sysplugins/smarty_internal_parsetree_template.php +++ b/src/ParseTree/Template.php @@ -1,10 +1,13 @@ subtrees)) { $this->subtrees = array_merge($this->subtrees, $subtree->subtrees); @@ -52,10 +55,10 @@ class Smarty_Internal_ParseTree_Template extends Smarty_Internal_ParseTree /** * Append array to subtree * - * @param \Smarty_Internal_Templateparser $parser - * @param \Smarty_Internal_ParseTree[] $array + * @param \Smarty\Parser\TemplateParser $parser + * @param Base[] $array */ - public function append_array(Smarty_Internal_Templateparser $parser, $array = array()) + public function append_array(\Smarty\Parser\TemplateParser $parser, $array = array()) { if (!empty($array)) { $this->subtrees = array_merge($this->subtrees, (array)$array); @@ -65,10 +68,10 @@ class Smarty_Internal_ParseTree_Template extends Smarty_Internal_ParseTree /** * Prepend array to subtree * - * @param \Smarty_Internal_Templateparser $parser - * @param \Smarty_Internal_ParseTree[] $array + * @param \Smarty\Parser\TemplateParser $parser + * @param Base[] $array */ - public function prepend_array(Smarty_Internal_Templateparser $parser, $array = array()) + public function prepend_array(\Smarty\Parser\TemplateParser $parser, $array = array()) { if (!empty($array)) { $this->subtrees = array_merge((array)$array, $this->subtrees); @@ -78,11 +81,11 @@ class Smarty_Internal_ParseTree_Template extends Smarty_Internal_ParseTree /** * Sanitize and merge subtree buffers together * - * @param \Smarty_Internal_Templateparser $parser + * @param \Smarty\Parser\TemplateParser $parser * * @return string template code content */ - public function to_smarty_php(Smarty_Internal_Templateparser $parser) + public function to_smarty_php(\Smarty\Parser\TemplateParser $parser) { $code = ''; @@ -111,7 +114,7 @@ class Smarty_Internal_ParseTree_Template extends Smarty_Internal_ParseTree break; case 'tag': foreach ($chunk['subtrees'] as $subtree) { - $text = $parser->compiler->appendCode($text, $subtree->to_smarty_php($parser)); + $text = $parser->compiler->appendCode($text, (string) $subtree->to_smarty_php($parser)); } $code .= $text; break; @@ -136,12 +139,12 @@ class Smarty_Internal_ParseTree_Template extends Smarty_Internal_ParseTree continue; } - if ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text + if ($this->subtrees[ $key ] instanceof Text && $this->subtrees[ $key ]->isToBeStripped()) { $newMode = 'textstripped'; - } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text) { + } elseif ($this->subtrees[ $key ] instanceof Text) { $newMode = 'text'; - } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Tag) { + } elseif ($this->subtrees[ $key ] instanceof Tag) { $newMode = 'tag'; } else { $newMode = 'other'; diff --git a/src/sysplugins/smarty_internal_parsetree_text.php b/src/ParseTree/Text.php similarity index 78% rename from src/sysplugins/smarty_internal_parsetree_text.php rename to src/ParseTree/Text.php index 58116c8..e613140 100644 --- a/src/sysplugins/smarty_internal_parsetree_text.php +++ b/src/ParseTree/Text.php @@ -1,20 +1,22 @@ data; } diff --git a/src/Parser/ConfigfileParser.php b/src/Parser/ConfigfileParser.php new file mode 100644 index 0000000..7997a09 --- /dev/null +++ b/src/Parser/ConfigfileParser.php @@ -0,0 +1,972 @@ + '\\', + '\'' => '\''); + + /** + * constructor + * + * @param Lexer $lex + * @param Configfile $compiler + */ + public function __construct(Lexer $lex, Configfile $compiler) + { + $this->lex = $lex; + $this->smarty = $compiler->getSmarty(); + $this->compiler = $compiler; + $this->configOverwrite = $this->smarty->config_overwrite; + $this->configReadHidden = $this->smarty->config_read_hidden; + } + + /** + * parse optional boolean keywords + * + * @param string $str + * + * @return bool + */ + private function parse_bool($str) + { + $str = strtolower($str); + if (in_array($str, array('on', 'yes', 'true'))) { + $res = true; + } else { + $res = false; + } + return $res; + } + + /** + * parse single quoted string + * remove outer quotes + * unescape inner quotes + * + * @param string $qstr + * + * @return string + */ + private static function parse_single_quoted_string($qstr) + { + $escaped_string = substr($qstr, 1, strlen($qstr) - 2); //remove outer quotes + + $ss = preg_split('/(\\\\.)/', $escaped_string, - 1, PREG_SPLIT_DELIM_CAPTURE); + + $str = ''; + foreach ($ss as $s) { + if (strlen($s) === 2 && $s[0] === '\\') { + if (isset(self::$escapes_single[$s[1]])) { + $s = self::$escapes_single[$s[1]]; + } + } + $str .= $s; + } + return $str; + } + + /** + * parse double quoted string + * + * @param string $qstr + * + * @return string + */ + private static function parse_double_quoted_string($qstr) + { + $inner_str = substr($qstr, 1, strlen($qstr) - 2); + return stripcslashes($inner_str); + } + + /** + * parse triple quoted string + * + * @param string $qstr + * + * @return string + */ + private static function parse_tripple_double_quoted_string($qstr) + { + return stripcslashes($qstr); + } + + /** + * set a config variable in target array + * + * @param array $var + * @param array $target_array + */ + private function set_var(array $var, array &$target_array) + { + $key = $var['key']; + $value = $var['value']; + + if ($this->configOverwrite || !isset($target_array['vars'][$key])) { + $target_array['vars'][$key] = $value; + } else { + settype($target_array['vars'][$key], 'array'); + $target_array['vars'][$key][] = $value; + } + } + + /** + * add config variable to global vars + * + * @param array $vars + */ + private function add_global_vars(array $vars) + { + if (!isset($this->compiler->config_data['vars'])) { + $this->compiler->config_data['vars'] = array(); + } + foreach ($vars as $var) { + $this->set_var($var, $this->compiler->config_data); + } + } + + /** + * add config variable to section + * + * @param string $section_name + * @param array $vars + */ + private function add_section_vars($section_name, array $vars) + { + if (!isset($this->compiler->config_data['sections'][$section_name]['vars'])) { + $this->compiler->config_data['sections'][$section_name]['vars'] = array(); + } + foreach ($vars as $var) { + $this->set_var($var, $this->compiler->config_data['sections'][$section_name]); + } + } + + const TPC_OPENB = 1; + const TPC_SECTION = 2; + const TPC_CLOSEB = 3; + const TPC_DOT = 4; + const TPC_ID = 5; + const TPC_EQUAL = 6; + const TPC_FLOAT = 7; + const TPC_INT = 8; + const TPC_BOOL = 9; + const TPC_SINGLE_QUOTED_STRING = 10; + const TPC_DOUBLE_QUOTED_STRING = 11; + const TPC_TRIPPLE_QUOTES = 12; + const TPC_TRIPPLE_TEXT = 13; + const TPC_TRIPPLE_QUOTES_END = 14; + const TPC_NAKED_STRING = 15; + const TPC_OTHER = 16; + const TPC_NEWLINE = 17; + const TPC_COMMENTSTART = 18; + const YY_NO_ACTION = 60; + const YY_ACCEPT_ACTION = 59; + const YY_ERROR_ACTION = 58; + + const YY_SZ_ACTTAB = 39; +public static $yy_action = array( + 24, 25, 26, 27, 28, 12, 15, 23, 31, 32, + 59, 8, 9, 3, 21, 22, 33, 13, 33, 13, + 14, 10, 18, 16, 30, 11, 17, 20, 34, 7, + 5, 1, 2, 29, 4, 19, 52, 35, 6, + ); + public static $yy_lookahead = array( + 7, 8, 9, 10, 11, 12, 5, 27, 15, 16, + 20, 21, 25, 23, 25, 26, 17, 18, 17, 18, + 2, 25, 4, 13, 14, 1, 15, 24, 17, 22, + 3, 23, 23, 14, 6, 2, 28, 17, 3, +); + const YY_SHIFT_USE_DFLT = -8; + const YY_SHIFT_MAX = 19; + public static $yy_shift_ofst = array( + -8, 1, 1, 1, -7, -1, -1, 24, -8, -8, + -8, 18, 10, 11, 27, 28, 19, 20, 33, 35, +); + const YY_REDUCE_USE_DFLT = -21; + const YY_REDUCE_MAX = 10; + public static $yy_reduce_ofst = array( + -10, -11, -11, -11, -20, -13, -4, 3, 7, 8, + 9, +); + public static $yyExpectedTokens = array( + array(), + array(5, 17, 18, ), + array(5, 17, 18, ), + array(5, 17, 18, ), + array(7, 8, 9, 10, 11, 12, 15, 16, ), + array(17, 18, ), + array(17, 18, ), + array(1, ), + array(), + array(), + array(), + array(2, 4, ), + array(13, 14, ), + array(15, 17, ), + array(3, ), + array(6, ), + array(14, ), + array(17, ), + array(2, ), + array(3, ), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), +); + public static $yy_default = array( + 44, 40, 41, 37, 58, 58, 58, 36, 39, 44, + 44, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 38, 42, 43, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, +); + const YYNOCODE = 29; + const YYSTACKDEPTH = 100; + const YYNSTATE = 36; + const YYNRULE = 22; + const YYERRORSYMBOL = 19; + const YYERRSYMDT = 'yy0'; + const YYFALLBACK = 0; + public static $yyFallback = array( + ); + public function Trace($TraceFILE, $zTracePrompt) + { + if (!$TraceFILE) { + $zTracePrompt = 0; + } elseif (!$zTracePrompt) { + $TraceFILE = 0; + } + $this->yyTraceFILE = $TraceFILE; + $this->yyTracePrompt = $zTracePrompt; + } + + public function PrintTrace() + { + $this->yyTraceFILE = fopen('php://output', 'w'); + $this->yyTracePrompt = '
'; + } + + public $yyTraceFILE; + public $yyTracePrompt; + public $yyidx; /* Index of top element in stack */ + public $yyerrcnt; /* Shifts left before out of the error */ + public $yystack = array(); /* The parser's stack */ + + public $yyTokenName = array( + '$', 'OPENB', 'SECTION', 'CLOSEB', + 'DOT', 'ID', 'EQUAL', 'FLOAT', + 'INT', 'BOOL', 'SINGLE_QUOTED_STRING', 'DOUBLE_QUOTED_STRING', + 'TRIPPLE_QUOTES', 'TRIPPLE_TEXT', 'TRIPPLE_QUOTES_END', 'NAKED_STRING', + 'OTHER', 'NEWLINE', 'COMMENTSTART', 'error', + 'start', 'global_vars', 'sections', 'var_list', + 'section', 'newline', 'var', 'value', + ); + + public static $yyRuleName = array( + 'start ::= global_vars sections', + 'global_vars ::= var_list', + 'sections ::= sections section', + 'sections ::=', + 'section ::= OPENB SECTION CLOSEB newline var_list', + 'section ::= OPENB DOT SECTION CLOSEB newline var_list', + 'var_list ::= var_list newline', + 'var_list ::= var_list var', + 'var_list ::=', + 'var ::= ID EQUAL value', + 'value ::= FLOAT', + 'value ::= INT', + 'value ::= BOOL', + 'value ::= SINGLE_QUOTED_STRING', + 'value ::= DOUBLE_QUOTED_STRING', + 'value ::= TRIPPLE_QUOTES TRIPPLE_TEXT TRIPPLE_QUOTES_END', + 'value ::= TRIPPLE_QUOTES TRIPPLE_QUOTES_END', + 'value ::= NAKED_STRING', + 'value ::= OTHER', + 'newline ::= NEWLINE', + 'newline ::= COMMENTSTART NEWLINE', + 'newline ::= COMMENTSTART NAKED_STRING NEWLINE', + ); + + public function tokenName($tokenType) + { + if ($tokenType === 0) { + return 'End of Input'; + } + if ($tokenType > 0 && $tokenType < count($this->yyTokenName)) { + return $this->yyTokenName[$tokenType]; + } else { + return 'Unknown'; + } + } + + public static function yy_destructor($yymajor, $yypminor) + { + switch ($yymajor) { + default: break; /* If no destructor action specified: do nothing */ + } + } + + public function yy_pop_parser_stack() + { + if (empty($this->yystack)) { + return; + } + $yytos = array_pop($this->yystack); + if ($this->yyTraceFILE && $this->yyidx >= 0) { + fwrite($this->yyTraceFILE, + $this->yyTracePrompt . 'Popping ' . $this->yyTokenName[$yytos->major] . + "\n"); + } + $yymajor = $yytos->major; + self::yy_destructor($yymajor, $yytos->minor); + $this->yyidx--; + + return $yymajor; + } + + public function __destruct() + { + while ($this->yystack !== Array()) { + $this->yy_pop_parser_stack(); + } + if (is_resource($this->yyTraceFILE)) { + fclose($this->yyTraceFILE); + } + } + + public function yy_get_expected_tokens($token) + { + static $res3 = array(); + static $res4 = array(); + $state = $this->yystack[$this->yyidx]->stateno; + $expected = self::$yyExpectedTokens[$state]; + if (isset($res3[$state][$token])) { + if ($res3[$state][$token]) { + return $expected; + } + } else { + if ($res3[$state][$token] = in_array($token, self::$yyExpectedTokens[$state], true)) { + return $expected; + } + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ === 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return array_unique($expected); + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno][1]; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno][0]); + if (isset(self::$yyExpectedTokens[$nextstate])) { + $expected = array_merge($expected, self::$yyExpectedTokens[$nextstate]); + if (isset($res4[$nextstate][$token])) { + if ($res4[$nextstate][$token]) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); + } + } else { + if ($res4[$nextstate][$token] = in_array($token, self::$yyExpectedTokens[$nextstate], true)) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); + } + } + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno][0]; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate === self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return array_unique($expected); + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return $expected; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + $this->yyidx = $yyidx; + $this->yystack = $stack; + + return array_unique($expected); + } + + public function yy_is_expected_token($token) + { + static $res = array(); + static $res2 = array(); + if ($token === 0) { + return true; // 0 is not part of this + } + $state = $this->yystack[$this->yyidx]->stateno; + if (isset($res[$state][$token])) { + if ($res[$state][$token]) { + return true; + } + } else { + if ($res[$state][$token] = in_array($token, self::$yyExpectedTokens[$state], true)) { + return true; + } + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ === 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return true; + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno][1]; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno][0]); + if (isset($res2[$nextstate][$token])) { + if ($res2[$nextstate][$token]) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + } else { + if ($res2[$nextstate][$token] = (isset(self::$yyExpectedTokens[$nextstate]) && in_array($token, self::$yyExpectedTokens[$nextstate], true))) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno][0]; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate === self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + if (!$token) { + // end of input: this is valid + return true; + } + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return false; + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return true; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + $this->yyidx = $yyidx; + $this->yystack = $stack; + + return true; + } + + public function yy_find_shift_action($iLookAhead) + { + $stateno = $this->yystack[$this->yyidx]->stateno; + + /* if ($this->yyidx < 0) return self::YY_NO_ACTION; */ + if (!isset(self::$yy_shift_ofst[$stateno])) { + // no shift actions + return self::$yy_default[$stateno]; + } + $i = self::$yy_shift_ofst[$stateno]; + if ($i === self::YY_SHIFT_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead === self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback) + && ($iFallback = self::$yyFallback[$iLookAhead]) != 0) { + if ($this->yyTraceFILE) { + fwrite($this->yyTraceFILE, $this->yyTracePrompt . 'FALLBACK ' . + $this->yyTokenName[$iLookAhead] . ' => ' . + $this->yyTokenName[$iFallback] . "\n"); + } + + return $this->yy_find_shift_action($iFallback); + } + + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + public function yy_find_reduce_action($stateno, $iLookAhead) + { + /* $stateno = $this->yystack[$this->yyidx]->stateno; */ + + if (!isset(self::$yy_reduce_ofst[$stateno])) { + return self::$yy_default[$stateno]; + } + $i = self::$yy_reduce_ofst[$stateno]; + if ($i === self::YY_REDUCE_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead === self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + public function yy_shift($yyNewState, $yyMajor, $yypMinor) + { + $this->yyidx++; + if ($this->yyidx >= self::YYSTACKDEPTH) { + $this->yyidx--; + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sStack Overflow!\n", $this->yyTracePrompt); + } + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } +// line 245 "src/Parser/ConfigfileParser.y" + + $this->internalError = true; + $this->compiler->trigger_config_file_error('Stack overflow in configfile parser'); + + return; + } + $yytos = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $yytos->stateno = $yyNewState; + $yytos->major = $yyMajor; + $yytos->minor = $yypMinor; + $this->yystack[] = $yytos; + if ($this->yyTraceFILE && $this->yyidx > 0) { + fprintf($this->yyTraceFILE, "%sShift %d\n", $this->yyTracePrompt, + $yyNewState); + fprintf($this->yyTraceFILE, "%sStack:", $this->yyTracePrompt); + for ($i = 1; $i <= $this->yyidx; $i++) { + fprintf($this->yyTraceFILE, " %s", + $this->yyTokenName[$this->yystack[$i]->major]); + } + fwrite($this->yyTraceFILE,"\n"); + } + } + + public static $yyRuleInfo = array( + array( 0 => 20, 1 => 2 ), + array( 0 => 21, 1 => 1 ), + array( 0 => 22, 1 => 2 ), + array( 0 => 22, 1 => 0 ), + array( 0 => 24, 1 => 5 ), + array( 0 => 24, 1 => 6 ), + array( 0 => 23, 1 => 2 ), + array( 0 => 23, 1 => 2 ), + array( 0 => 23, 1 => 0 ), + array( 0 => 26, 1 => 3 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 27, 1 => 3 ), + array( 0 => 27, 1 => 2 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 27, 1 => 1 ), + array( 0 => 25, 1 => 1 ), + array( 0 => 25, 1 => 2 ), + array( 0 => 25, 1 => 3 ), + ); + + public static $yyReduceMap = array( + 0 => 0, + 2 => 0, + 3 => 0, + 19 => 0, + 20 => 0, + 21 => 0, + 1 => 1, + 4 => 4, + 5 => 5, + 6 => 6, + 7 => 7, + 8 => 8, + 9 => 9, + 10 => 10, + 11 => 11, + 12 => 12, + 13 => 13, + 14 => 14, + 15 => 15, + 16 => 16, + 17 => 17, + 18 => 17, + ); +// line 251 "src/Parser/ConfigfileParser.y" + public function yy_r0(){ + $this->_retvalue = null; + } +// line 256 "src/Parser/ConfigfileParser.y" + public function yy_r1(){ + $this->add_global_vars($this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = null; + } +// line 270 "src/Parser/ConfigfileParser.y" + public function yy_r4(){ + $this->add_section_vars($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = null; + } +// line 275 "src/Parser/ConfigfileParser.y" + public function yy_r5(){ + if ($this->configReadHidden) { + $this->add_section_vars($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + 0]->minor); + } + $this->_retvalue = null; + } +// line 283 "src/Parser/ConfigfileParser.y" + public function yy_r6(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 287 "src/Parser/ConfigfileParser.y" + public function yy_r7(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + 0]->minor)); + } +// line 291 "src/Parser/ConfigfileParser.y" + public function yy_r8(){ + $this->_retvalue = array(); + } +// line 297 "src/Parser/ConfigfileParser.y" + public function yy_r9(){ + $this->_retvalue = array('key' => $this->yystack[$this->yyidx + -2]->minor, 'value' => $this->yystack[$this->yyidx + 0]->minor); + } +// line 302 "src/Parser/ConfigfileParser.y" + public function yy_r10(){ + $this->_retvalue = (float) $this->yystack[$this->yyidx + 0]->minor; + } +// line 306 "src/Parser/ConfigfileParser.y" + public function yy_r11(){ + $this->_retvalue = (int) $this->yystack[$this->yyidx + 0]->minor; + } +// line 310 "src/Parser/ConfigfileParser.y" + public function yy_r12(){ + $this->_retvalue = $this->parse_bool($this->yystack[$this->yyidx + 0]->minor); + } +// line 314 "src/Parser/ConfigfileParser.y" + public function yy_r13(){ + $this->_retvalue = self::parse_single_quoted_string($this->yystack[$this->yyidx + 0]->minor); + } +// line 318 "src/Parser/ConfigfileParser.y" + public function yy_r14(){ + $this->_retvalue = self::parse_double_quoted_string($this->yystack[$this->yyidx + 0]->minor); + } +// line 322 "src/Parser/ConfigfileParser.y" + public function yy_r15(){ + $this->_retvalue = self::parse_tripple_double_quoted_string($this->yystack[$this->yyidx + -1]->minor); + } +// line 326 "src/Parser/ConfigfileParser.y" + public function yy_r16(){ + $this->_retvalue = ''; + } +// line 330 "src/Parser/ConfigfileParser.y" + public function yy_r17(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } + + private $_retvalue; + + public function yy_reduce($yyruleno) + { + if ($this->yyTraceFILE && $yyruleno >= 0 + && $yyruleno < count(self::$yyRuleName)) { + fprintf($this->yyTraceFILE, "%sReduce (%d) [%s].\n", + $this->yyTracePrompt, $yyruleno, + self::$yyRuleName[$yyruleno]); + } + + $this->_retvalue = $yy_lefthand_side = null; + if (isset(self::$yyReduceMap[$yyruleno])) { + // call the action + $this->_retvalue = null; + $this->{'yy_r' . self::$yyReduceMap[$yyruleno]}(); + $yy_lefthand_side = $this->_retvalue; + } + $yygoto = self::$yyRuleInfo[$yyruleno][0]; + $yysize = self::$yyRuleInfo[$yyruleno][1]; + $this->yyidx -= $yysize; + for ($i = $yysize; $i; $i--) { + // pop all of the right-hand side parameters + array_pop($this->yystack); + } + $yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto); + if ($yyact < self::YYNSTATE) { + if (!$this->yyTraceFILE && $yysize) { + $this->yyidx++; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = $yyact; + $x->major = $yygoto; + $x->minor = $yy_lefthand_side; + $this->yystack[$this->yyidx] = $x; + } else { + $this->yy_shift($yyact, $yygoto, $yy_lefthand_side); + } + } elseif ($yyact === self::YYNSTATE + self::YYNRULE + 1) { + $this->yy_accept(); + } + } + + public function yy_parse_failed() + { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sFail!\n", $this->yyTracePrompt); + } while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + } + + public function yy_syntax_error($yymajor, $TOKEN) + { +// line 238 "src/Parser/ConfigfileParser.y" + + $this->internalError = true; + $this->yymajor = $yymajor; + $this->compiler->trigger_config_file_error(); + } + + public function yy_accept() + { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sAccept!\n", $this->yyTracePrompt); + } while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } +// line 231 "src/Parser/ConfigfileParser.y" + + $this->successful = !$this->internalError; + $this->internalError = false; + $this->retvalue = $this->_retvalue; + } + + public function doParse($yymajor, $yytokenvalue) + { + $yyerrorhit = 0; /* True if yymajor has invoked an error */ + + if ($this->yyidx === null || $this->yyidx < 0) { + $this->yyidx = 0; + $this->yyerrcnt = -1; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = 0; + $x->major = 0; + $this->yystack = array(); + $this->yystack[] = $x; + } + $yyendofinput = ($yymajor==0); + + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sInput %s\n", + $this->yyTracePrompt, $this->yyTokenName[$yymajor]); + } + + do { + $yyact = $this->yy_find_shift_action($yymajor); + if ($yymajor < self::YYERRORSYMBOL && + !$this->yy_is_expected_token($yymajor)) { + // force a syntax error + $yyact = self::YY_ERROR_ACTION; + } + if ($yyact < self::YYNSTATE) { + $this->yy_shift($yyact, $yymajor, $yytokenvalue); + $this->yyerrcnt--; + if ($yyendofinput && $this->yyidx >= 0) { + $yymajor = 0; + } else { + $yymajor = self::YYNOCODE; + } + } elseif ($yyact < self::YYNSTATE + self::YYNRULE) { + $this->yy_reduce($yyact - self::YYNSTATE); + } elseif ($yyact === self::YY_ERROR_ACTION) { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sSyntax Error!\n", + $this->yyTracePrompt); + } + if (self::YYERRORSYMBOL) { + if ($this->yyerrcnt < 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $yymx = $this->yystack[$this->yyidx]->major; + if ($yymx === self::YYERRORSYMBOL || $yyerrorhit) { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sDiscard input token %s\n", + $this->yyTracePrompt, $this->yyTokenName[$yymajor]); + } + $this->yy_destructor($yymajor, $yytokenvalue); + $yymajor = self::YYNOCODE; + } else { + while ($this->yyidx >= 0 && + $yymx !== self::YYERRORSYMBOL && + ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE + ){ + $this->yy_pop_parser_stack(); + } + if ($this->yyidx < 0 || $yymajor==0) { + $this->yy_destructor($yymajor, $yytokenvalue); + $this->yy_parse_failed(); + $yymajor = self::YYNOCODE; + } elseif ($yymx !== self::YYERRORSYMBOL) { + $u2 = 0; + $this->yy_shift($yyact, self::YYERRORSYMBOL, $u2); + } + } + $this->yyerrcnt = 3; + $yyerrorhit = 1; + } else { + if ($this->yyerrcnt <= 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $this->yyerrcnt = 3; + $this->yy_destructor($yymajor, $yytokenvalue); + if ($yyendofinput) { + $this->yy_parse_failed(); + } + $yymajor = self::YYNOCODE; + } + } else { + $this->yy_accept(); + $yymajor = self::YYNOCODE; + } + } while ($yymajor !== self::YYNOCODE && $this->yyidx >= 0); + } +} + diff --git a/src/Parser/ConfigfileParser.y b/src/Parser/ConfigfileParser.y new file mode 100644 index 0000000..23afc2d --- /dev/null +++ b/src/Parser/ConfigfileParser.y @@ -0,0 +1,352 @@ +/** +* ConfigfileParser +* +* This is the config file parser +* +* +* @package Smarty +* @subpackage Config +* @author Uwe Tews +*/ +%name TPC_ +%declare_class { + +namespace Smarty\Parser; + +use \Smarty\Lexer\ConfigfileLexer as Lexer; +use \Smarty\Compiler\Configfile as Configfile; + +/** +* Smarty Internal Plugin Configfileparse +* +* This is the config file parser. +* It is generated from the ConfigfileParser.y file +* @package Smarty +* @subpackage Compiler +* @author Uwe Tews +*/ +class ConfigfileParser +} +%include_class +{ + /** + * result status + * + * @var bool + */ + public $successful = true; + /** + * return value + * + * @var mixed + */ + public $retvalue = 0; + /** + * @var + */ + public $yymajor; + /** + * lexer object + * + * @var Lexer + */ + private $lex; + /** + * internal error flag + * + * @var bool + */ + private $internalError = false; + /** + * compiler object + * + * @var Configfile + */ + public $compiler = null; + /** + * smarty object + * + * @var Smarty + */ + public $smarty = null; + /** + * copy of config_overwrite property + * + * @var bool + */ + private $configOverwrite = false; + /** + * copy of config_read_hidden property + * + * @var bool + */ + private $configReadHidden = false; + /** + * helper map + * + * @var array + */ + private static $escapes_single = array('\\' => '\\', + '\'' => '\''); + + /** + * constructor + * + * @param Lexer $lex + * @param Configfile $compiler + */ + public function __construct(Lexer $lex, Configfile $compiler) + { + $this->lex = $lex; + $this->smarty = $compiler->getSmarty(); + $this->compiler = $compiler; + $this->configOverwrite = $this->smarty->config_overwrite; + $this->configReadHidden = $this->smarty->config_read_hidden; + } + + /** + * parse optional boolean keywords + * + * @param string $str + * + * @return bool + */ + private function parse_bool($str) + { + $str = strtolower($str); + if (in_array($str, array('on', 'yes', 'true'))) { + $res = true; + } else { + $res = false; + } + return $res; + } + + /** + * parse single quoted string + * remove outer quotes + * unescape inner quotes + * + * @param string $qstr + * + * @return string + */ + private static function parse_single_quoted_string($qstr) + { + $escaped_string = substr($qstr, 1, strlen($qstr) - 2); //remove outer quotes + + $ss = preg_split('/(\\\\.)/', $escaped_string, - 1, PREG_SPLIT_DELIM_CAPTURE); + + $str = ''; + foreach ($ss as $s) { + if (strlen($s) === 2 && $s[0] === '\\') { + if (isset(self::$escapes_single[$s[1]])) { + $s = self::$escapes_single[$s[1]]; + } + } + $str .= $s; + } + return $str; + } + + /** + * parse double quoted string + * + * @param string $qstr + * + * @return string + */ + private static function parse_double_quoted_string($qstr) + { + $inner_str = substr($qstr, 1, strlen($qstr) - 2); + return stripcslashes($inner_str); + } + + /** + * parse triple quoted string + * + * @param string $qstr + * + * @return string + */ + private static function parse_tripple_double_quoted_string($qstr) + { + return stripcslashes($qstr); + } + + /** + * set a config variable in target array + * + * @param array $var + * @param array $target_array + */ + private function set_var(array $var, array &$target_array) + { + $key = $var['key']; + $value = $var['value']; + + if ($this->configOverwrite || !isset($target_array['vars'][$key])) { + $target_array['vars'][$key] = $value; + } else { + settype($target_array['vars'][$key], 'array'); + $target_array['vars'][$key][] = $value; + } + } + + /** + * add config variable to global vars + * + * @param array $vars + */ + private function add_global_vars(array $vars) + { + if (!isset($this->compiler->config_data['vars'])) { + $this->compiler->config_data['vars'] = array(); + } + foreach ($vars as $var) { + $this->set_var($var, $this->compiler->config_data); + } + } + + /** + * add config variable to section + * + * @param string $section_name + * @param array $vars + */ + private function add_section_vars($section_name, array $vars) + { + if (!isset($this->compiler->config_data['sections'][$section_name]['vars'])) { + $this->compiler->config_data['sections'][$section_name]['vars'] = array(); + } + foreach ($vars as $var) { + $this->set_var($var, $this->compiler->config_data['sections'][$section_name]); + } + } +} + +%token_prefix TPC_ + +%parse_accept +{ + $this->successful = !$this->internalError; + $this->internalError = false; + $this->retvalue = $this->_retvalue; +} + +%syntax_error +{ + $this->internalError = true; + $this->yymajor = $yymajor; + $this->compiler->trigger_config_file_error(); +} + +%stack_overflow +{ + $this->internalError = true; + $this->compiler->trigger_config_file_error('Stack overflow in configfile parser'); +} + +// Complete config file +start(res) ::= global_vars sections. { + res = null; +} + +// Global vars +global_vars(res) ::= var_list(vl). { + $this->add_global_vars(vl); + res = null; +} + +// Sections +sections(res) ::= sections section. { + res = null; +} + +sections(res) ::= . { + res = null; +} + +section(res) ::= OPENB SECTION(i) CLOSEB newline var_list(vars). { + $this->add_section_vars(i, vars); + res = null; +} + +section(res) ::= OPENB DOT SECTION(i) CLOSEB newline var_list(vars). { + if ($this->configReadHidden) { + $this->add_section_vars(i, vars); + } + res = null; +} + +// Var list +var_list(res) ::= var_list(vl) newline. { + res = vl; +} + +var_list(res) ::= var_list(vl) var(v). { + res = array_merge(vl, array(v)); +} + +var_list(res) ::= . { + res = array(); +} + + +// Var +var(res) ::= ID(id) EQUAL value(v). { + res = array('key' => id, 'value' => v); +} + + +value(res) ::= FLOAT(i). { + res = (float) i; +} + +value(res) ::= INT(i). { + res = (int) i; +} + +value(res) ::= BOOL(i). { + res = $this->parse_bool(i); +} + +value(res) ::= SINGLE_QUOTED_STRING(i). { + res = self::parse_single_quoted_string(i); +} + +value(res) ::= DOUBLE_QUOTED_STRING(i). { + res = self::parse_double_quoted_string(i); +} + +value(res) ::= TRIPPLE_QUOTES(i) TRIPPLE_TEXT(c) TRIPPLE_QUOTES_END(ii). { + res = self::parse_tripple_double_quoted_string(c); +} + +value(res) ::= TRIPPLE_QUOTES(i) TRIPPLE_QUOTES_END(ii). { + res = ''; +} + +value(res) ::= NAKED_STRING(i). { + res = i; +} + +// NOTE: this is not a valid rule +// It is added hier to produce a usefull error message on a missing '='; +value(res) ::= OTHER(i). { + res = i; +} + + +// Newline and comments +newline(res) ::= NEWLINE. { + res = null; +} + +newline(res) ::= COMMENTSTART NEWLINE. { + res = null; +} + +newline(res) ::= COMMENTSTART NAKED_STRING NEWLINE. { + res = null; +} diff --git a/src/Parser/TemplateParser.php b/src/Parser/TemplateParser.php new file mode 100644 index 0000000..1e087c5 --- /dev/null +++ b/src/Parser/TemplateParser.php @@ -0,0 +1,3051 @@ + +*/ +class TemplateParser +{ +// line 35 "src/Parser/TemplateParser.y" + + const ERR1 = 'Security error: Call to private object member not allowed'; + const ERR2 = 'Security error: Call to dynamic object member not allowed'; + + /** + * result status + * + * @var bool + */ + public $successful = true; + + /** + * return value + * + * @var mixed + */ + public $retvalue = 0; + + /** + * @var + */ + public $yymajor; + + /** + * last index of array variable + * + * @var mixed + */ + public $last_index; + + /** + * last variable name + * + * @var string + */ + public $last_variable; + + /** + * root parse tree buffer + * + * @var TemplateParseTree + */ + public $root_buffer; + + /** + * current parse tree object + * + * @var \Smarty\ParseTree\Base + */ + public $current_buffer; + + /** + * lexer object + * + * @var Lexer + */ + public $lex; + + /** + * internal error flag + * + * @var bool + */ + private $internalError = false; + + /** + * {strip} status + * + * @var bool + */ + public $strip = false; + /** + * compiler object + * + * @var TemplateCompiler + */ + public $compiler = null; + + /** + * smarty object + * + * @var \Smarty\Smarty + */ + public $smarty = null; + + /** + * template object + * + * @var \Smarty\Template + */ + public $template = null; + + /** + * block nesting level + * + * @var int + */ + public $block_nesting_level = 0; + + /** + * security object + * + * @var \Smarty\Security + */ + public $security = null; + + /** + * template prefix array + * + * @var \Smarty\ParseTree\Base[] + */ + public $template_prefix = array(); + + /** + * template prefix array + * + * @var \Smarty\ParseTree\Base[] + */ + public $template_postfix = array(); + + /** + * constructor + * + * @param Lexer $lex + * @param TemplateCompiler $compiler + */ + public function __construct(Lexer $lex, TemplateCompiler $compiler) + { + $this->lex = $lex; + $this->compiler = $compiler; + $this->template = $this->compiler->getTemplate(); + $this->smarty = $this->template->getSmarty(); + $this->security = $this->smarty->security_policy ?? false; + $this->current_buffer = $this->root_buffer = new TemplateParseTree(); + } + + /** + * insert PHP code in current buffer + * + * @param string $code + */ + public function insertPhpCode($code) + { + $this->current_buffer->append_subtree($this, new Tag($this, $code)); + } + + /** + * error rundown + * + */ + public function errorRunDown() + { + while ($this->yystack !== array()) { + $this->yy_pop_parser_stack(); + } + if (is_resource($this->yyTraceFILE)) { + fclose($this->yyTraceFILE); + } + } + + /** + * merge PHP code with prefix code and return parse tree tag object + * + * @param string $code + * + * @return Tag + */ + private function mergePrefixCode($code) + { + $tmp = ''; + foreach ($this->compiler->prefix_code as $preCode) { + $tmp .= $preCode; + } + $this->compiler->prefix_code = array(); + $tmp .= $code; + return new Tag($this, $this->compiler->processNocacheCode($tmp)); + } + + + const TP_VERT = 1; + const TP_COLON = 2; + const TP_TEXT = 3; + const TP_STRIPON = 4; + const TP_STRIPOFF = 5; + const TP_LITERALSTART = 6; + const TP_LITERALEND = 7; + const TP_LITERAL = 8; + const TP_SIMPELOUTPUT = 9; + const TP_SIMPLETAG = 10; + const TP_SMARTYBLOCKCHILDPARENT = 11; + const TP_LDEL = 12; + const TP_RDEL = 13; + const TP_DOLLARID = 14; + const TP_EQUAL = 15; + const TP_ID = 16; + const TP_PTR = 17; + const TP_LDELIF = 18; + const TP_LDELFOR = 19; + const TP_SEMICOLON = 20; + const TP_INCDEC = 21; + const TP_TO = 22; + const TP_STEP = 23; + const TP_LDELFOREACH = 24; + const TP_SPACE = 25; + const TP_AS = 26; + const TP_APTR = 27; + const TP_LDELSETFILTER = 28; + const TP_CLOSETAG = 29; + const TP_LDELSLASH = 30; + const TP_ATTR = 31; + const TP_INTEGER = 32; + const TP_COMMA = 33; + const TP_OPENP = 34; + const TP_CLOSEP = 35; + const TP_MATH = 36; + const TP_UNIMATH = 37; + const TP_ISIN = 38; + const TP_QMARK = 39; + const TP_NOT = 40; + const TP_TYPECAST = 41; + const TP_HEX = 42; + const TP_DOT = 43; + const TP_INSTANCEOF = 44; + const TP_SINGLEQUOTESTRING = 45; + const TP_DOUBLECOLON = 46; + const TP_NAMESPACE = 47; + const TP_AT = 48; + const TP_HATCH = 49; + const TP_OPENB = 50; + const TP_CLOSEB = 51; + const TP_DOLLAR = 52; + const TP_LOGOP = 53; + const TP_SLOGOP = 54; + const TP_TLOGOP = 55; + const TP_SINGLECOND = 56; + const TP_ARRAYOPEN = 57; + const TP_QUOTE = 58; + const TP_BACKTICK = 59; + const YY_NO_ACTION = 527; + const YY_ACCEPT_ACTION = 526; + const YY_ERROR_ACTION = 525; + + const YY_SZ_ACTTAB = 2372; +public static $yy_action = array( + 33, 197, 264, 299, 176, 298, 259, 242, 243, 244, + 1, 259, 135, 232, 199, 354, 6, 84, 495, 217, + 331, 354, 109, 104, 393, 248, 212, 256, 213, 51, + 219, 393, 21, 393, 51, 43, 393, 32, 44, 45, + 273, 221, 393, 277, 393, 200, 393, 83, 4, 136, + 295, 226, 149, 99, 220, 5, 52, 242, 243, 244, + 1, 307, 132, 211, 190, 9, 6, 84, 241, 217, + 211, 126, 109, 150, 261, 252, 212, 256, 213, 137, + 205, 98, 21, 313, 83, 43, 13, 295, 44, 45, + 273, 221, 260, 230, 197, 200, 293, 83, 4, 321, + 295, 35, 149, 86, 309, 5, 52, 242, 243, 244, + 1, 146, 97, 387, 82, 231, 6, 84, 14, 217, + 138, 251, 109, 148, 15, 387, 212, 256, 213, 452, + 219, 387, 21, 251, 439, 43, 452, 252, 44, 45, + 273, 221, 3, 277, 99, 200, 439, 83, 4, 252, + 295, 259, 526, 96, 252, 5, 52, 242, 243, 244, + 1, 136, 134, 262, 199, 103, 6, 84, 155, 217, + 252, 279, 109, 112, 51, 439, 212, 256, 213, 127, + 219, 316, 21, 99, 228, 43, 314, 439, 44, 45, + 273, 221, 318, 277, 263, 200, 83, 83, 4, 295, + 295, 46, 22, 280, 40, 5, 52, 242, 243, 244, + 1, 20, 134, 189, 191, 266, 6, 84, 254, 217, + 250, 19, 109, 152, 141, 253, 212, 256, 213, 197, + 219, 267, 21, 251, 251, 43, 175, 298, 44, 45, + 273, 221, 151, 277, 108, 200, 91, 83, 4, 87, + 295, 295, 251, 354, 180, 5, 52, 242, 243, 244, + 1, 259, 133, 140, 199, 354, 6, 84, 307, 217, + 211, 354, 109, 181, 197, 39, 212, 256, 213, 286, + 219, 14, 11, 94, 51, 43, 88, 15, 44, 45, + 273, 221, 153, 277, 143, 200, 92, 83, 4, 139, + 295, 295, 251, 154, 104, 5, 52, 242, 243, 244, + 1, 303, 134, 251, 186, 173, 6, 84, 36, 217, + 99, 126, 109, 181, 227, 285, 212, 256, 213, 137, + 208, 98, 21, 127, 180, 43, 13, 295, 44, 45, + 273, 221, 181, 277, 232, 200, 293, 83, 4, 112, + 295, 234, 196, 297, 104, 5, 52, 242, 243, 244, + 1, 29, 134, 224, 184, 156, 6, 84, 468, 217, + 197, 468, 109, 197, 23, 468, 212, 256, 213, 264, + 219, 18, 21, 175, 298, 43, 215, 15, 44, 45, + 273, 221, 232, 277, 170, 200, 168, 83, 4, 233, + 295, 295, 104, 144, 99, 5, 52, 242, 243, 244, + 1, 259, 134, 251, 199, 157, 6, 84, 26, 217, + 161, 181, 109, 181, 255, 439, 212, 256, 213, 178, + 185, 240, 21, 112, 51, 43, 164, 439, 44, 45, + 273, 221, 174, 277, 222, 200, 251, 83, 4, 305, + 295, 41, 42, 281, 12, 5, 52, 242, 243, 244, + 1, 197, 136, 163, 199, 167, 6, 84, 288, 289, + 290, 291, 109, 17, 304, 251, 212, 256, 213, 330, + 219, 16, 21, 258, 171, 47, 180, 25, 44, 45, + 273, 221, 39, 277, 93, 200, 255, 83, 4, 328, + 295, 41, 42, 281, 12, 5, 52, 242, 243, 244, + 1, 181, 136, 439, 199, 214, 6, 84, 288, 289, + 290, 291, 109, 177, 298, 439, 212, 256, 213, 28, + 219, 8, 21, 209, 194, 43, 89, 295, 44, 45, + 273, 221, 452, 277, 24, 200, 296, 83, 4, 452, + 295, 7, 440, 239, 240, 5, 52, 283, 210, 211, + 247, 95, 90, 106, 440, 188, 100, 81, 10, 9, + 254, 310, 98, 19, 294, 268, 269, 253, 195, 158, + 113, 169, 276, 201, 278, 172, 284, 293, 283, 210, + 211, 247, 197, 90, 106, 238, 187, 100, 58, 24, + 34, 322, 220, 98, 358, 159, 268, 269, 225, 223, + 317, 245, 179, 276, 201, 278, 14, 284, 293, 246, + 110, 283, 15, 211, 249, 439, 111, 106, 220, 188, + 100, 81, 24, 257, 323, 254, 98, 439, 19, 268, + 269, 118, 253, 265, 270, 272, 276, 201, 278, 7, + 284, 293, 283, 220, 211, 274, 292, 111, 85, 229, + 198, 116, 70, 275, 319, 160, 329, 98, 162, 320, + 268, 269, 36, 145, 216, 37, 332, 276, 201, 278, + 303, 284, 293, 41, 42, 281, 12, 303, 303, 283, + 303, 211, 204, 312, 111, 303, 303, 198, 116, 70, + 288, 289, 290, 291, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 303, 303, 283, 303, 211, 303, 303, 105, 203, + 312, 198, 119, 49, 303, 117, 303, 303, 98, 303, + 254, 268, 269, 19, 303, 303, 303, 253, 276, 201, + 278, 303, 284, 293, 303, 283, 14, 211, 147, 303, + 111, 303, 15, 198, 119, 65, 197, 303, 303, 303, + 98, 303, 468, 268, 269, 468, 197, 303, 355, 468, + 276, 201, 278, 303, 284, 293, 303, 303, 389, 283, + 355, 211, 207, 303, 111, 303, 355, 198, 119, 65, + 389, 303, 303, 303, 98, 303, 389, 268, 269, 303, + 197, 468, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 303, 386, 283, 303, 211, 202, 303, 111, 303, + 303, 198, 116, 70, 386, 303, 303, 303, 98, 303, + 386, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 303, 303, 283, 303, 211, + 303, 303, 111, 303, 311, 198, 119, 65, 303, 303, + 303, 303, 98, 303, 303, 268, 269, 303, 303, 303, + 303, 303, 276, 201, 278, 303, 284, 293, 303, 303, + 303, 283, 303, 211, 206, 303, 105, 303, 303, 198, + 119, 56, 303, 233, 303, 303, 98, 303, 303, 268, + 269, 303, 303, 303, 303, 303, 276, 201, 278, 303, + 284, 293, 303, 283, 303, 211, 303, 303, 111, 303, + 303, 198, 115, 62, 303, 303, 303, 303, 98, 303, + 303, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 303, 303, 283, 303, 211, + 303, 303, 111, 303, 303, 193, 114, 57, 303, 303, + 303, 303, 98, 303, 303, 268, 269, 303, 303, 303, + 303, 303, 276, 201, 278, 303, 284, 293, 303, 283, + 303, 211, 303, 303, 111, 303, 303, 198, 101, 80, + 303, 303, 303, 303, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 303, 303, 283, 303, 211, 303, 303, 111, 303, + 303, 198, 102, 79, 303, 303, 303, 303, 98, 303, + 303, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 283, 303, 211, 303, 303, + 111, 303, 303, 198, 119, 53, 303, 303, 303, 303, + 98, 303, 303, 268, 269, 303, 303, 303, 303, 303, + 276, 201, 278, 303, 284, 293, 303, 303, 303, 283, + 303, 211, 303, 303, 111, 303, 303, 198, 119, 64, + 303, 303, 303, 303, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 283, 303, 211, 303, 303, 111, 303, 303, 198, + 101, 54, 303, 303, 303, 303, 98, 303, 303, 268, + 269, 303, 303, 303, 303, 303, 276, 201, 278, 303, + 284, 293, 303, 303, 303, 283, 303, 211, 303, 303, + 111, 303, 303, 198, 119, 63, 303, 303, 303, 303, + 98, 303, 303, 268, 269, 303, 303, 303, 303, 303, + 276, 201, 278, 303, 284, 293, 303, 283, 303, 211, + 303, 303, 111, 303, 303, 198, 119, 55, 303, 303, + 303, 303, 98, 303, 303, 268, 269, 303, 303, 303, + 303, 303, 276, 201, 278, 303, 284, 293, 303, 303, + 303, 283, 303, 211, 303, 303, 111, 303, 303, 198, + 119, 56, 303, 303, 303, 303, 98, 303, 303, 268, + 269, 303, 303, 303, 303, 303, 276, 201, 278, 303, + 284, 293, 303, 283, 303, 211, 303, 303, 111, 303, + 303, 198, 119, 66, 303, 303, 303, 303, 98, 303, + 303, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 303, 303, 283, 303, 211, + 303, 303, 111, 303, 303, 198, 119, 67, 303, 303, + 303, 303, 98, 303, 303, 268, 269, 303, 303, 303, + 303, 303, 276, 201, 278, 303, 284, 293, 303, 283, + 303, 211, 303, 303, 111, 303, 303, 198, 119, 68, + 303, 303, 303, 303, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 303, 303, 283, 303, 211, 303, 303, 111, 303, + 303, 198, 119, 69, 303, 303, 303, 303, 98, 303, + 303, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 283, 303, 211, 303, 303, + 111, 303, 303, 198, 119, 71, 303, 303, 303, 303, + 98, 303, 303, 268, 269, 303, 303, 303, 303, 303, + 276, 201, 278, 303, 284, 293, 303, 303, 303, 283, + 303, 211, 303, 303, 111, 303, 303, 192, 119, 59, + 303, 303, 303, 303, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 283, 303, 211, 303, 303, 111, 303, 303, 198, + 119, 60, 303, 303, 303, 303, 98, 303, 303, 268, + 269, 303, 303, 303, 303, 303, 276, 201, 278, 303, + 284, 293, 303, 303, 303, 283, 303, 211, 303, 303, + 111, 303, 303, 198, 119, 61, 303, 303, 303, 303, + 98, 303, 303, 268, 269, 303, 303, 303, 303, 303, + 276, 201, 278, 303, 284, 293, 303, 283, 303, 211, + 303, 303, 111, 303, 303, 198, 119, 72, 303, 303, + 303, 303, 98, 303, 303, 268, 269, 303, 303, 303, + 303, 303, 276, 201, 278, 303, 284, 293, 303, 303, + 303, 283, 303, 211, 303, 303, 111, 303, 303, 198, + 119, 73, 303, 303, 303, 303, 98, 303, 303, 268, + 269, 303, 303, 303, 303, 303, 276, 201, 278, 303, + 284, 293, 303, 283, 303, 211, 303, 303, 111, 303, + 303, 198, 119, 74, 303, 303, 303, 303, 98, 303, + 303, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 303, 303, 283, 303, 211, + 303, 303, 111, 303, 303, 198, 119, 75, 303, 303, + 303, 303, 98, 303, 303, 268, 269, 303, 303, 303, + 303, 303, 276, 201, 278, 303, 284, 293, 303, 283, + 303, 211, 303, 303, 111, 303, 303, 198, 119, 76, + 303, 303, 303, 303, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 303, 303, 283, 303, 211, 303, 303, 111, 303, + 303, 198, 119, 77, 303, 303, 303, 303, 98, 303, + 303, 268, 269, 303, 303, 303, 303, 303, 276, 201, + 278, 303, 284, 293, 303, 283, 303, 211, 303, 303, + 111, 303, 303, 198, 119, 78, 303, 303, 303, 303, + 98, 303, 303, 268, 269, 303, 303, 303, 303, 303, + 276, 201, 278, 303, 284, 293, 303, 303, 303, 283, + 303, 211, 303, 303, 111, 303, 303, 198, 119, 48, + 303, 303, 303, 303, 98, 303, 303, 268, 269, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 303, 283, 303, 211, 303, 303, 111, 303, 303, 198, + 119, 50, 303, 303, 303, 303, 98, 303, 303, 268, + 269, 303, 303, 303, 303, 303, 276, 201, 278, 303, + 284, 293, 308, 303, 303, 303, 303, 303, 242, 243, + 244, 2, 303, 306, 303, 303, 303, 6, 84, 254, + 303, 303, 19, 109, 303, 303, 253, 212, 256, 213, + 303, 303, 38, 303, 14, 14, 303, 303, 303, 165, + 15, 15, 303, 303, 303, 41, 42, 281, 12, 251, + 303, 303, 46, 22, 280, 40, 303, 301, 27, 303, + 303, 308, 288, 289, 290, 291, 303, 242, 243, 244, + 2, 303, 306, 303, 303, 303, 6, 84, 303, 303, + 303, 303, 109, 303, 14, 303, 212, 256, 213, 303, + 15, 142, 303, 303, 303, 41, 42, 281, 12, 303, + 303, 251, 303, 303, 46, 22, 280, 40, 303, 303, + 303, 303, 288, 289, 290, 291, 302, 27, 303, 303, + 235, 236, 237, 130, 303, 303, 242, 243, 244, 1, + 468, 303, 303, 468, 303, 6, 84, 468, 452, 303, + 303, 109, 303, 303, 303, 212, 256, 213, 283, 303, + 211, 303, 303, 111, 303, 303, 198, 131, 303, 303, + 303, 303, 303, 98, 452, 303, 303, 452, 303, 468, + 303, 452, 326, 276, 201, 278, 303, 284, 293, 303, + 218, 303, 303, 283, 303, 211, 303, 468, 111, 303, + 468, 198, 125, 3, 468, 452, 303, 303, 98, 271, + 303, 303, 303, 303, 303, 303, 303, 282, 276, 201, + 278, 303, 284, 293, 283, 303, 211, 303, 303, 111, + 166, 452, 198, 129, 452, 303, 468, 303, 452, 98, + 251, 303, 303, 46, 22, 280, 40, 303, 303, 276, + 201, 278, 303, 284, 293, 303, 218, 303, 303, 283, + 303, 211, 303, 468, 111, 303, 468, 198, 120, 35, + 468, 452, 303, 303, 98, 271, 303, 303, 303, 303, + 303, 303, 303, 303, 276, 201, 278, 303, 284, 293, + 283, 303, 211, 303, 303, 111, 303, 452, 198, 121, + 452, 303, 468, 303, 452, 98, 303, 303, 303, 303, + 303, 303, 303, 303, 303, 276, 201, 278, 303, 284, + 293, 303, 218, 303, 303, 283, 303, 211, 303, 468, + 111, 303, 468, 198, 122, 303, 468, 452, 303, 303, + 98, 271, 303, 303, 303, 303, 303, 303, 303, 303, + 276, 201, 278, 303, 284, 293, 283, 303, 211, 303, + 303, 111, 303, 452, 198, 123, 452, 303, 468, 303, + 452, 98, 303, 303, 303, 303, 303, 303, 303, 303, + 303, 276, 201, 278, 303, 284, 293, 303, 30, 303, + 303, 283, 303, 211, 218, 468, 111, 303, 468, 198, + 124, 468, 468, 452, 468, 303, 98, 271, 468, 452, + 303, 303, 303, 271, 303, 303, 276, 201, 278, 303, + 284, 293, 283, 402, 211, 303, 303, 111, 303, 452, + 198, 128, 452, 303, 468, 452, 452, 98, 452, 303, + 468, 303, 452, 287, 303, 107, 325, 276, 201, 278, + 303, 284, 293, 303, 303, 439, 303, 402, 402, 402, + 402, 41, 42, 281, 12, 303, 303, 439, 303, 41, + 42, 281, 12, 303, 402, 402, 402, 402, 288, 289, + 290, 291, 41, 42, 281, 12, 288, 289, 290, 291, + 300, 324, 41, 42, 281, 12, 182, 315, 303, 288, + 289, 290, 291, 183, 303, 303, 303, 303, 303, 288, + 289, 290, 291, 41, 42, 281, 12, 31, 303, 41, + 42, 281, 12, 303, 327, 303, 41, 42, 281, 12, + 288, 289, 290, 291, 303, 303, 288, 289, 290, 291, + 303, 303, 303, 288, 289, 290, 291, 41, 42, 281, + 12, 41, 42, 281, 12, 303, 303, 303, 303, 303, + 303, 303, 303, 303, 288, 289, 290, 291, 288, 289, + 290, 291, + ); + public static $yy_lookahead = array( + 2, 1, 97, 13, 99, 100, 21, 9, 10, 11, + 12, 21, 14, 70, 16, 25, 18, 19, 1, 21, + 77, 31, 24, 80, 13, 69, 28, 29, 30, 44, + 32, 20, 34, 22, 44, 37, 25, 39, 40, 41, + 42, 43, 31, 45, 33, 47, 35, 49, 50, 14, + 52, 16, 96, 17, 43, 57, 58, 9, 10, 11, + 12, 65, 14, 67, 16, 33, 18, 19, 65, 21, + 67, 70, 24, 96, 73, 98, 28, 29, 30, 43, + 32, 80, 34, 51, 49, 37, 50, 52, 40, 41, + 42, 43, 91, 45, 1, 47, 95, 49, 50, 51, + 52, 15, 96, 107, 108, 57, 58, 9, 10, 11, + 12, 72, 14, 13, 16, 15, 18, 19, 25, 21, + 80, 82, 24, 72, 31, 25, 28, 29, 30, 43, + 32, 31, 34, 82, 34, 37, 50, 98, 40, 41, + 42, 43, 15, 45, 17, 47, 46, 49, 50, 98, + 52, 21, 61, 62, 98, 57, 58, 9, 10, 11, + 12, 14, 14, 16, 16, 80, 18, 19, 96, 21, + 98, 93, 24, 46, 44, 34, 28, 29, 30, 101, + 32, 51, 34, 17, 43, 37, 101, 46, 40, 41, + 42, 43, 51, 45, 47, 47, 49, 49, 50, 52, + 52, 85, 86, 87, 88, 57, 58, 9, 10, 11, + 12, 12, 14, 14, 16, 16, 18, 19, 9, 21, + 82, 12, 24, 72, 72, 16, 28, 29, 30, 1, + 32, 32, 34, 82, 82, 37, 99, 100, 40, 41, + 42, 43, 72, 45, 79, 47, 76, 49, 50, 80, + 52, 52, 82, 13, 103, 57, 58, 9, 10, 11, + 12, 21, 14, 14, 16, 25, 18, 19, 65, 21, + 67, 31, 24, 103, 1, 2, 28, 29, 30, 51, + 32, 25, 34, 34, 44, 37, 80, 31, 40, 41, + 42, 43, 72, 45, 70, 47, 76, 49, 50, 14, + 52, 52, 82, 72, 80, 57, 58, 9, 10, 11, + 12, 108, 14, 82, 16, 76, 18, 19, 15, 21, + 17, 70, 24, 103, 73, 93, 28, 29, 30, 43, + 32, 80, 34, 101, 103, 37, 50, 52, 40, 41, + 42, 43, 103, 45, 70, 47, 95, 49, 50, 46, + 52, 77, 78, 69, 80, 57, 58, 9, 10, 11, + 12, 12, 14, 14, 16, 16, 18, 19, 9, 21, + 1, 12, 24, 1, 2, 16, 28, 29, 30, 97, + 32, 25, 34, 99, 100, 37, 17, 31, 40, 41, + 42, 43, 70, 45, 76, 47, 76, 49, 50, 77, + 52, 52, 80, 72, 17, 57, 58, 9, 10, 11, + 12, 21, 14, 82, 16, 96, 18, 19, 27, 21, + 96, 103, 24, 103, 104, 34, 28, 29, 30, 6, + 32, 8, 34, 46, 44, 37, 72, 46, 40, 41, + 42, 43, 14, 45, 16, 47, 82, 49, 50, 59, + 52, 36, 37, 38, 39, 57, 58, 9, 10, 11, + 12, 1, 14, 96, 16, 72, 18, 19, 53, 54, + 55, 56, 24, 15, 59, 82, 28, 29, 30, 21, + 32, 20, 34, 16, 76, 37, 103, 27, 40, 41, + 42, 43, 2, 45, 33, 47, 104, 49, 50, 14, + 52, 36, 37, 38, 39, 57, 58, 9, 10, 11, + 12, 103, 14, 34, 16, 48, 18, 19, 53, 54, + 55, 56, 24, 99, 100, 46, 28, 29, 30, 12, + 32, 34, 34, 63, 64, 37, 96, 52, 40, 41, + 42, 43, 43, 45, 33, 47, 35, 49, 50, 50, + 52, 34, 34, 7, 8, 57, 58, 65, 66, 67, + 68, 81, 70, 71, 46, 73, 74, 75, 34, 33, + 9, 35, 80, 12, 100, 83, 84, 16, 64, 96, + 46, 81, 90, 91, 92, 81, 94, 95, 65, 66, + 67, 68, 1, 70, 71, 7, 73, 74, 75, 33, + 15, 35, 43, 80, 13, 96, 83, 84, 17, 48, + 51, 13, 16, 90, 91, 92, 25, 94, 95, 13, + 16, 65, 31, 67, 68, 34, 70, 71, 43, 73, + 74, 75, 33, 16, 35, 9, 80, 46, 12, 83, + 84, 16, 16, 16, 14, 16, 90, 91, 92, 34, + 94, 95, 65, 43, 67, 32, 16, 70, 16, 16, + 73, 74, 75, 32, 51, 49, 16, 80, 49, 51, + 83, 84, 15, 26, 48, 22, 35, 90, 91, 92, + 109, 94, 95, 36, 37, 38, 39, 109, 109, 65, + 109, 67, 105, 106, 70, 109, 109, 73, 74, 75, + 53, 54, 55, 56, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 109, 109, 65, 109, 67, 109, 109, 70, 105, + 106, 73, 74, 75, 109, 77, 109, 109, 80, 109, + 9, 83, 84, 12, 109, 109, 109, 16, 90, 91, + 92, 109, 94, 95, 109, 65, 25, 67, 27, 109, + 70, 109, 31, 73, 74, 75, 1, 109, 109, 109, + 80, 109, 9, 83, 84, 12, 1, 109, 13, 16, + 90, 91, 92, 109, 94, 95, 109, 109, 13, 65, + 25, 67, 102, 109, 70, 109, 31, 73, 74, 75, + 25, 109, 109, 109, 80, 109, 31, 83, 84, 109, + 1, 48, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 109, 13, 65, 109, 67, 102, 109, 70, 109, + 109, 73, 74, 75, 25, 109, 109, 109, 80, 109, + 31, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 109, 109, 65, 109, 67, + 109, 109, 70, 109, 106, 73, 74, 75, 109, 109, + 109, 109, 80, 109, 109, 83, 84, 109, 109, 109, + 109, 109, 90, 91, 92, 109, 94, 95, 109, 109, + 109, 65, 109, 67, 102, 109, 70, 109, 109, 73, + 74, 75, 109, 77, 109, 109, 80, 109, 109, 83, + 84, 109, 109, 109, 109, 109, 90, 91, 92, 109, + 94, 95, 109, 65, 109, 67, 109, 109, 70, 109, + 109, 73, 74, 75, 109, 109, 109, 109, 80, 109, + 109, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 109, 109, 65, 109, 67, + 109, 109, 70, 109, 109, 73, 74, 75, 109, 109, + 109, 109, 80, 109, 109, 83, 84, 109, 109, 109, + 109, 109, 90, 91, 92, 109, 94, 95, 109, 65, + 109, 67, 109, 109, 70, 109, 109, 73, 74, 75, + 109, 109, 109, 109, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 109, 109, 65, 109, 67, 109, 109, 70, 109, + 109, 73, 74, 75, 109, 109, 109, 109, 80, 109, + 109, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 65, 109, 67, 109, 109, + 70, 109, 109, 73, 74, 75, 109, 109, 109, 109, + 80, 109, 109, 83, 84, 109, 109, 109, 109, 109, + 90, 91, 92, 109, 94, 95, 109, 109, 109, 65, + 109, 67, 109, 109, 70, 109, 109, 73, 74, 75, + 109, 109, 109, 109, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 65, 109, 67, 109, 109, 70, 109, 109, 73, + 74, 75, 109, 109, 109, 109, 80, 109, 109, 83, + 84, 109, 109, 109, 109, 109, 90, 91, 92, 109, + 94, 95, 109, 109, 109, 65, 109, 67, 109, 109, + 70, 109, 109, 73, 74, 75, 109, 109, 109, 109, + 80, 109, 109, 83, 84, 109, 109, 109, 109, 109, + 90, 91, 92, 109, 94, 95, 109, 65, 109, 67, + 109, 109, 70, 109, 109, 73, 74, 75, 109, 109, + 109, 109, 80, 109, 109, 83, 84, 109, 109, 109, + 109, 109, 90, 91, 92, 109, 94, 95, 109, 109, + 109, 65, 109, 67, 109, 109, 70, 109, 109, 73, + 74, 75, 109, 109, 109, 109, 80, 109, 109, 83, + 84, 109, 109, 109, 109, 109, 90, 91, 92, 109, + 94, 95, 109, 65, 109, 67, 109, 109, 70, 109, + 109, 73, 74, 75, 109, 109, 109, 109, 80, 109, + 109, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 109, 109, 65, 109, 67, + 109, 109, 70, 109, 109, 73, 74, 75, 109, 109, + 109, 109, 80, 109, 109, 83, 84, 109, 109, 109, + 109, 109, 90, 91, 92, 109, 94, 95, 109, 65, + 109, 67, 109, 109, 70, 109, 109, 73, 74, 75, + 109, 109, 109, 109, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 109, 109, 65, 109, 67, 109, 109, 70, 109, + 109, 73, 74, 75, 109, 109, 109, 109, 80, 109, + 109, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 65, 109, 67, 109, 109, + 70, 109, 109, 73, 74, 75, 109, 109, 109, 109, + 80, 109, 109, 83, 84, 109, 109, 109, 109, 109, + 90, 91, 92, 109, 94, 95, 109, 109, 109, 65, + 109, 67, 109, 109, 70, 109, 109, 73, 74, 75, + 109, 109, 109, 109, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 65, 109, 67, 109, 109, 70, 109, 109, 73, + 74, 75, 109, 109, 109, 109, 80, 109, 109, 83, + 84, 109, 109, 109, 109, 109, 90, 91, 92, 109, + 94, 95, 109, 109, 109, 65, 109, 67, 109, 109, + 70, 109, 109, 73, 74, 75, 109, 109, 109, 109, + 80, 109, 109, 83, 84, 109, 109, 109, 109, 109, + 90, 91, 92, 109, 94, 95, 109, 65, 109, 67, + 109, 109, 70, 109, 109, 73, 74, 75, 109, 109, + 109, 109, 80, 109, 109, 83, 84, 109, 109, 109, + 109, 109, 90, 91, 92, 109, 94, 95, 109, 109, + 109, 65, 109, 67, 109, 109, 70, 109, 109, 73, + 74, 75, 109, 109, 109, 109, 80, 109, 109, 83, + 84, 109, 109, 109, 109, 109, 90, 91, 92, 109, + 94, 95, 109, 65, 109, 67, 109, 109, 70, 109, + 109, 73, 74, 75, 109, 109, 109, 109, 80, 109, + 109, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 109, 109, 65, 109, 67, + 109, 109, 70, 109, 109, 73, 74, 75, 109, 109, + 109, 109, 80, 109, 109, 83, 84, 109, 109, 109, + 109, 109, 90, 91, 92, 109, 94, 95, 109, 65, + 109, 67, 109, 109, 70, 109, 109, 73, 74, 75, + 109, 109, 109, 109, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 109, 109, 65, 109, 67, 109, 109, 70, 109, + 109, 73, 74, 75, 109, 109, 109, 109, 80, 109, + 109, 83, 84, 109, 109, 109, 109, 109, 90, 91, + 92, 109, 94, 95, 109, 65, 109, 67, 109, 109, + 70, 109, 109, 73, 74, 75, 109, 109, 109, 109, + 80, 109, 109, 83, 84, 109, 109, 109, 109, 109, + 90, 91, 92, 109, 94, 95, 109, 109, 109, 65, + 109, 67, 109, 109, 70, 109, 109, 73, 74, 75, + 109, 109, 109, 109, 80, 109, 109, 83, 84, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 109, 65, 109, 67, 109, 109, 70, 109, 109, 73, + 74, 75, 109, 109, 109, 109, 80, 109, 109, 83, + 84, 109, 109, 109, 109, 109, 90, 91, 92, 109, + 94, 95, 3, 109, 109, 109, 109, 109, 9, 10, + 11, 12, 109, 14, 109, 109, 109, 18, 19, 9, + 109, 109, 12, 24, 109, 109, 16, 28, 29, 30, + 109, 109, 23, 109, 25, 25, 109, 109, 109, 72, + 31, 31, 109, 109, 109, 36, 37, 38, 39, 82, + 109, 109, 85, 86, 87, 88, 109, 58, 59, 109, + 109, 3, 53, 54, 55, 56, 109, 9, 10, 11, + 12, 109, 14, 109, 109, 109, 18, 19, 109, 109, + 109, 109, 24, 109, 25, 109, 28, 29, 30, 109, + 31, 72, 109, 109, 109, 36, 37, 38, 39, 109, + 109, 82, 109, 109, 85, 86, 87, 88, 109, 109, + 109, 109, 53, 54, 55, 56, 58, 59, 109, 109, + 3, 4, 5, 6, 109, 109, 9, 10, 11, 12, + 9, 109, 109, 12, 109, 18, 19, 16, 17, 109, + 109, 24, 109, 109, 109, 28, 29, 30, 65, 109, + 67, 109, 109, 70, 109, 109, 73, 74, 109, 109, + 109, 109, 109, 80, 43, 109, 109, 46, 109, 48, + 109, 50, 89, 90, 91, 92, 109, 94, 95, 109, + 2, 109, 109, 65, 109, 67, 109, 9, 70, 109, + 12, 73, 74, 15, 16, 17, 109, 109, 80, 21, + 109, 109, 109, 109, 109, 109, 109, 89, 90, 91, + 92, 109, 94, 95, 65, 109, 67, 109, 109, 70, + 72, 43, 73, 74, 46, 109, 48, 109, 50, 80, + 82, 109, 109, 85, 86, 87, 88, 109, 109, 90, + 91, 92, 109, 94, 95, 109, 2, 109, 109, 65, + 109, 67, 109, 9, 70, 109, 12, 73, 74, 15, + 16, 17, 109, 109, 80, 21, 109, 109, 109, 109, + 109, 109, 109, 109, 90, 91, 92, 109, 94, 95, + 65, 109, 67, 109, 109, 70, 109, 43, 73, 74, + 46, 109, 48, 109, 50, 80, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 90, 91, 92, 109, 94, + 95, 109, 2, 109, 109, 65, 109, 67, 109, 9, + 70, 109, 12, 73, 74, 109, 16, 17, 109, 109, + 80, 21, 109, 109, 109, 109, 109, 109, 109, 109, + 90, 91, 92, 109, 94, 95, 65, 109, 67, 109, + 109, 70, 109, 43, 73, 74, 46, 109, 48, 109, + 50, 80, 109, 109, 109, 109, 109, 109, 109, 109, + 109, 90, 91, 92, 109, 94, 95, 109, 2, 109, + 109, 65, 109, 67, 2, 9, 70, 109, 12, 73, + 74, 9, 16, 17, 12, 109, 80, 21, 16, 17, + 109, 109, 109, 21, 109, 109, 90, 91, 92, 109, + 94, 95, 65, 2, 67, 109, 109, 70, 109, 43, + 73, 74, 46, 109, 48, 43, 50, 80, 46, 109, + 48, 109, 50, 51, 109, 20, 13, 90, 91, 92, + 109, 94, 95, 109, 109, 34, 109, 36, 37, 38, + 39, 36, 37, 38, 39, 109, 109, 46, 109, 36, + 37, 38, 39, 109, 53, 54, 55, 56, 53, 54, + 55, 56, 36, 37, 38, 39, 53, 54, 55, 56, + 13, 35, 36, 37, 38, 39, 13, 51, 109, 53, + 54, 55, 56, 13, 109, 109, 109, 109, 109, 53, + 54, 55, 56, 36, 37, 38, 39, 2, 109, 36, + 37, 38, 39, 109, 13, 109, 36, 37, 38, 39, + 53, 54, 55, 56, 109, 109, 53, 54, 55, 56, + 109, 109, 109, 53, 54, 55, 56, 36, 37, 38, + 39, 36, 37, 38, 39, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 53, 54, 55, 56, 53, 54, + 55, 56, +); + const YY_SHIFT_USE_DFLT = -16; + const YY_SHIFT_MAX = 234; + public static $yy_shift_ofst = array( + -16, 98, 98, 148, 198, 198, 248, 148, 148, 198, + 148, 248, -2, 48, 298, 148, 148, 148, 298, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 348, 148, 148, 148, 148, 398, 148, 148, 148, 448, + 498, 498, 498, 498, 498, 498, 498, 498, 1819, 1869, + 1869, 147, 1809, 2225, 647, 2233, 2256, 2246, 2277, 415, + 2283, 2290, 2315, 2311, 465, 465, 465, 465, 465, 465, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, + 465, 465, 591, 35, 249, 93, 1868, 731, 1820, 36, + 127, 93, 93, 249, 249, 273, 1927, 1988, 561, 349, + 765, 775, 809, 209, 209, 303, 256, 285, 256, 356, + 369, 387, 428, 428, 228, 372, 460, 256, 0, 0, + 0, 0, 0, 0, 0, 0, 166, 166, 17, 0, + -16, -16, 2192, 2054, 2120, 2186, 1931, 199, 626, 359, + 86, 256, 256, 458, 256, 485, 256, 485, 256, 286, + 286, 256, 256, 256, 256, 286, 517, 286, 286, 286, + 499, 286, 499, 286, 256, 256, 256, 256, 0, 490, + 0, 0, 490, 0, 497, 166, 166, 166, -16, -16, + -16, -16, -16, -16, 2221, 11, 100, -10, 240, 763, + 141, 391, 390, 130, 423, 546, 461, 467, -15, 479, + 518, 534, 511, 536, 32, 559, 566, 599, 585, 588, + 598, 606, 596, 604, 617, 625, 627, 630, 629, 610, + 623, 631, 615, 640, 497, 642, 616, 619, 643, 613, + 618, 650, 657, 641, 653, +); + const YY_REDUCE_USE_DFLT = -96; + const YY_REDUCE_MAX = 183; + public static $yy_reduce_ofst = array( + 91, 492, 523, 556, 587, 624, 658, 690, 724, 758, + 792, 826, 858, 892, 924, 958, 990, 1024, 1056, 1090, + 1122, 1156, 1188, 1222, 1254, 1288, 1320, 1354, 1386, 1420, + 1452, 1486, 1518, 1552, 1584, 1618, 1650, 1684, 1716, 1893, + 1928, 1959, 1994, 2025, 2060, 2091, 2126, 2157, 1777, 1829, + 1958, 1, -4, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 170, 251, 274, 220, 203, 39, 51, -95, + 284, 151, 231, -57, 322, 320, 3, -44, -23, 85, + 239, 239, 239, 72, -23, 137, 152, 224, 331, 364, + 318, 137, 78, 232, 239, 239, 239, 393, 408, 239, + 239, 239, 239, 239, 239, 239, 137, 424, 239, 239, + 470, 239, 6, 6, 6, 6, 6, 40, 56, 6, + 6, 138, 138, 165, 138, 169, 138, 206, 138, 282, + 282, 138, 138, 138, 138, 282, 319, 282, 282, 282, + 324, 282, 367, 282, 138, 138, 138, 138, 383, 392, + 383, 383, 392, 383, 440, 474, 474, 474, 514, 480, + 500, 504, 483, 509, +); + public static $yyExpectedTokens = array( + array(), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(2, 9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 39, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 51, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 21, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(9, 10, 11, 12, 14, 16, 18, 19, 24, 28, 29, 30, 32, 34, 37, 40, 41, 42, 43, 45, 47, 49, 50, 52, 57, 58, ), + array(23, 25, 31, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(25, 31, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(25, 31, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(14, 16, 47, 49, 52, ), + array(3, 9, 10, 11, 12, 14, 18, 19, 24, 28, 29, 30, 58, 59, ), + array(20, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(26, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(13, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(35, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 51, 53, 54, 55, 56, ), + array(13, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, 59, ), + array(13, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(13, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(2, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(13, 36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(36, 37, 38, 39, 53, 54, 55, 56, ), + array(1, 13, 17, 25, 31, 34, 46, ), + array(14, 16, 49, 52, ), + array(14, 34, 52, ), + array(1, 25, 31, ), + array(3, 9, 10, 11, 12, 14, 18, 19, 24, 28, 29, 30, 58, 59, ), + array(9, 12, 16, 25, 27, 31, ), + array(9, 12, 16, 25, 31, ), + array(17, 43, 50, ), + array(15, 17, 46, ), + array(1, 25, 31, ), + array(1, 25, 31, ), + array(14, 34, 52, ), + array(14, 34, 52, ), + array(1, 2, ), + array(3, 4, 5, 6, 9, 10, 11, 12, 18, 19, 24, 28, 29, 30, ), + array(2, 9, 12, 15, 16, 17, 21, 43, 46, 48, 50, ), + array(9, 12, 16, 48, ), + array(12, 14, 16, 52, ), + array(1, 13, 25, 31, ), + array(1, 13, 25, 31, ), + array(1, 13, 25, 31, ), + array(9, 12, 16, ), + array(9, 12, 16, ), + array(15, 17, 46, ), + array(25, 31, ), + array(14, 52, ), + array(25, 31, ), + array(25, 31, ), + array(1, 17, ), + array(17, 46, ), + array(14, 16, ), + array(14, 16, ), + array(1, 51, ), + array(1, 2, ), + array(1, 27, ), + array(25, 31, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(1, ), + array(17, ), + array(17, ), + array(1, ), + array(1, ), + array(), + array(), + array(2, 9, 12, 16, 17, 21, 43, 46, 48, 50, 51, ), + array(2, 9, 12, 15, 16, 17, 21, 43, 46, 48, 50, ), + array(2, 9, 12, 16, 17, 21, 43, 46, 48, 50, ), + array(2, 9, 12, 16, 17, 21, 43, 46, 48, 50, ), + array(9, 12, 16, 17, 43, 46, 48, 50, ), + array(12, 14, 16, 32, 52, ), + array(9, 12, 16, 48, ), + array(9, 12, 16, ), + array(15, 43, 50, ), + array(25, 31, ), + array(25, 31, ), + array(15, 21, ), + array(25, 31, ), + array(14, 52, ), + array(25, 31, ), + array(14, 52, ), + array(25, 31, ), + array(43, 50, ), + array(43, 50, ), + array(25, 31, ), + array(25, 31, ), + array(25, 31, ), + array(25, 31, ), + array(43, 50, ), + array(12, 34, ), + array(43, 50, ), + array(43, 50, ), + array(43, 50, ), + array(43, 50, ), + array(43, 50, ), + array(43, 50, ), + array(43, 50, ), + array(25, 31, ), + array(25, 31, ), + array(25, 31, ), + array(25, 31, ), + array(1, ), + array(2, ), + array(1, ), + array(1, ), + array(2, ), + array(1, ), + array(34, ), + array(17, ), + array(17, ), + array(17, ), + array(), + array(), + array(), + array(), + array(), + array(), + array(2, 34, 36, 37, 38, 39, 46, 53, 54, 55, 56, ), + array(13, 20, 22, 25, 31, 33, 35, 43, ), + array(13, 15, 25, 31, 34, 46, ), + array(13, 21, 25, 31, 44, ), + array(13, 21, 25, 31, 44, ), + array(9, 12, 16, 48, ), + array(34, 43, 46, 51, ), + array(27, 34, 46, ), + array(21, 44, 59, ), + array(21, 44, 51, ), + array(6, 8, ), + array(7, 8, ), + array(20, 33, ), + array(16, 48, ), + array(21, 44, ), + array(34, 46, ), + array(34, 46, ), + array(34, 46, ), + array(33, 35, ), + array(33, 35, ), + array(33, 51, ), + array(43, 51, ), + array(33, 35, ), + array(33, 35, ), + array(15, 43, ), + array(7, ), + array(13, ), + array(13, ), + array(16, ), + array(16, ), + array(16, ), + array(16, ), + array(16, ), + array(14, ), + array(16, ), + array(43, ), + array(32, ), + array(32, ), + array(34, ), + array(16, ), + array(34, ), + array(16, ), + array(49, ), + array(49, ), + array(16, ), + array(51, ), + array(51, ), + array(16, ), + array(15, ), + array(35, ), + array(22, ), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), + array(), +); + public static $yy_default = array( + 343, 525, 525, 525, 510, 510, 525, 487, 487, 525, + 487, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 383, 362, + 383, 525, 525, 525, 388, 525, 525, 525, 356, 525, + 525, 525, 525, 525, 367, 486, 406, 413, 485, 511, + 513, 512, 412, 414, 411, 415, 390, 394, 395, 385, + 388, 356, 426, 525, 525, 383, 525, 383, 383, 500, + 442, 383, 383, 525, 525, 374, 333, 441, 452, 525, + 397, 397, 397, 452, 452, 442, 383, 525, 383, 383, + 377, 442, 525, 525, 397, 397, 397, 364, 379, 397, + 404, 417, 418, 419, 405, 410, 442, 497, 417, 403, + 341, 494, 441, 441, 441, 441, 441, 525, 454, 452, + 468, 353, 363, 525, 366, 525, 371, 525, 372, 449, + 450, 357, 359, 360, 361, 478, 452, 477, 480, 479, + 445, 446, 447, 448, 373, 369, 370, 365, 375, 488, + 378, 380, 489, 435, 452, 474, 501, 498, 341, 493, + 493, 493, 452, 452, 426, 422, 426, 416, 416, 453, + 426, 426, 416, 416, 339, 525, 525, 525, 416, 426, + 436, 525, 525, 525, 525, 422, 525, 525, 422, 525, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 422, + 424, 525, 499, 525, 468, 525, 525, 525, 525, 525, + 431, 525, 525, 525, 391, 334, 335, 336, 337, 338, + 340, 342, 344, 345, 346, 347, 348, 349, 350, 352, + 381, 382, 470, 471, 472, 492, 376, 490, 491, 420, + 429, 430, 439, 440, 451, 455, 456, 457, 398, 399, + 400, 401, 402, 421, 423, 425, 427, 431, 432, 433, + 407, 408, 409, 434, 437, 438, 465, 463, 502, 503, + 504, 505, 443, 444, 476, 469, 484, 351, 475, 521, + 522, 514, 515, 516, 519, 518, 520, 523, 524, 517, + 507, 509, 508, 506, 481, 466, 464, 462, 459, 460, + 461, 467, 482, 483, 428, 458, 496, 473, 468, 384, + 368, 392, 396, +); + const YYNOCODE = 110; + const YYSTACKDEPTH = 500; + const YYNSTATE = 333; + const YYNRULE = 192; + const YYERRORSYMBOL = 60; + const YYERRSYMDT = 'yy0'; + const YYFALLBACK = 0; + public static $yyFallback = array( + ); + public function Trace($TraceFILE, $zTracePrompt) + { + if (!$TraceFILE) { + $zTracePrompt = 0; + } elseif (!$zTracePrompt) { + $TraceFILE = 0; + } + $this->yyTraceFILE = $TraceFILE; + $this->yyTracePrompt = $zTracePrompt; + } + + public function PrintTrace() + { + $this->yyTraceFILE = fopen('php://output', 'w'); + $this->yyTracePrompt = '
'; + } + + public $yyTraceFILE; + public $yyTracePrompt; + public $yyidx; /* Index of top element in stack */ + public $yyerrcnt; /* Shifts left before out of the error */ + public $yystack = array(); /* The parser's stack */ + + public $yyTokenName = array( + '$', 'VERT', 'COLON', 'TEXT', + 'STRIPON', 'STRIPOFF', 'LITERALSTART', 'LITERALEND', + 'LITERAL', 'SIMPELOUTPUT', 'SIMPLETAG', 'SMARTYBLOCKCHILDPARENT', + 'LDEL', 'RDEL', 'DOLLARID', 'EQUAL', + 'ID', 'PTR', 'LDELIF', 'LDELFOR', + 'SEMICOLON', 'INCDEC', 'TO', 'STEP', + 'LDELFOREACH', 'SPACE', 'AS', 'APTR', + 'LDELSETFILTER', 'CLOSETAG', 'LDELSLASH', 'ATTR', + 'INTEGER', 'COMMA', 'OPENP', 'CLOSEP', + 'MATH', 'UNIMATH', 'ISIN', 'QMARK', + 'NOT', 'TYPECAST', 'HEX', 'DOT', + 'INSTANCEOF', 'SINGLEQUOTESTRING', 'DOUBLECOLON', 'NAMESPACE', + 'AT', 'HATCH', 'OPENB', 'CLOSEB', + 'DOLLAR', 'LOGOP', 'SLOGOP', 'TLOGOP', + 'SINGLECOND', 'ARRAYOPEN', 'QUOTE', 'BACKTICK', + 'error', 'start', 'template', 'literal_e2', + 'literal_e1', 'smartytag', 'tagbody', 'tag', + 'outattr', 'eqoutattr', 'varindexed', 'output', + 'attributes', 'variable', 'value', 'expr', + 'modifierlist', 'statement', 'statements', 'foraction', + 'varvar', 'modparameters', 'attribute', 'nullcoalescing', + 'ternary', 'tlop', 'lop', 'scond', + 'isin', 'array', 'function', 'ns1', + 'doublequoted_with_quotes', 'static_class_access', 'arraydef', 'object', + 'arrayindex', 'indexdef', 'varvarele', 'objectchain', + 'objectelement', 'method', 'params', 'modifier', + 'modparameter', 'arrayelements', 'arrayelement', 'doublequoted', + 'doublequotedcontent', + ); + + public static $yyRuleName = array( + 'start ::= template', + 'template ::= template TEXT', + 'template ::= template STRIPON', + 'template ::= template STRIPOFF', + 'template ::= template LITERALSTART literal_e2 LITERALEND', + 'literal_e2 ::= literal_e1 LITERALSTART literal_e1 LITERALEND', + 'literal_e2 ::= literal_e1', + 'literal_e1 ::= literal_e1 LITERAL', + 'literal_e1 ::=', + 'template ::= template smartytag', + 'template ::=', + 'smartytag ::= SIMPELOUTPUT', + 'smartytag ::= SIMPLETAG', + 'smartytag ::= SMARTYBLOCKCHILDPARENT', + 'smartytag ::= LDEL tagbody RDEL', + 'smartytag ::= tag RDEL', + 'tagbody ::= outattr', + 'tagbody ::= DOLLARID eqoutattr', + 'tagbody ::= varindexed eqoutattr', + 'eqoutattr ::= EQUAL outattr', + 'outattr ::= output attributes', + 'output ::= variable', + 'output ::= value', + 'output ::= expr', + 'tag ::= LDEL ID attributes', + 'tag ::= LDEL ID', + 'tag ::= LDEL ID modifierlist attributes', + 'tag ::= LDEL ID PTR ID attributes', + 'tag ::= LDEL ID PTR ID modifierlist attributes', + 'tag ::= LDELIF expr', + 'tag ::= LDELIF expr attributes', + 'tag ::= LDELIF statement', + 'tag ::= LDELIF statement attributes', + 'tag ::= LDELFOR statements SEMICOLON expr SEMICOLON varindexed foraction attributes', + 'foraction ::= EQUAL expr', + 'foraction ::= INCDEC', + 'tag ::= LDELFOR statement TO expr attributes', + 'tag ::= LDELFOR statement TO expr STEP expr attributes', + 'tag ::= LDELFOREACH SPACE expr AS varvar attributes', + 'tag ::= LDELFOREACH SPACE expr AS varvar APTR varvar attributes', + 'tag ::= LDELFOREACH attributes', + 'tag ::= LDELSETFILTER ID modparameters', + 'tag ::= LDELSETFILTER ID modparameters modifierlist', + 'smartytag ::= CLOSETAG', + 'tag ::= LDELSLASH ID', + 'tag ::= LDELSLASH ID modifierlist', + 'tag ::= LDELSLASH ID PTR ID', + 'tag ::= LDELSLASH ID PTR ID modifierlist', + 'attributes ::= attributes attribute', + 'attributes ::= attribute', + 'attributes ::=', + 'attribute ::= SPACE ID EQUAL ID', + 'attribute ::= ATTR expr', + 'attribute ::= ATTR value', + 'attribute ::= SPACE ID', + 'attribute ::= SPACE expr', + 'attribute ::= SPACE value', + 'attribute ::= SPACE INTEGER EQUAL expr', + 'statements ::= statement', + 'statements ::= statements COMMA statement', + 'statement ::= DOLLARID EQUAL INTEGER', + 'statement ::= DOLLARID EQUAL expr', + 'statement ::= varindexed EQUAL expr', + 'statement ::= OPENP statement CLOSEP', + 'expr ::= value', + 'expr ::= nullcoalescing', + 'expr ::= ternary', + 'expr ::= INCDEC DOLLARID', + 'expr ::= DOLLARID INCDEC', + 'expr ::= DOLLARID COLON ID', + 'expr ::= expr MATH value', + 'expr ::= expr UNIMATH value', + 'expr ::= expr tlop value', + 'expr ::= expr lop expr', + 'expr ::= expr scond', + 'isin ::= ISIN', + 'expr ::= expr isin array', + 'expr ::= expr isin value', + 'nullcoalescing ::= expr QMARK QMARK expr', + 'ternary ::= expr QMARK DOLLARID COLON expr', + 'ternary ::= expr QMARK value COLON expr', + 'ternary ::= expr QMARK expr COLON expr', + 'ternary ::= expr QMARK COLON expr', + 'value ::= variable', + 'value ::= UNIMATH value', + 'value ::= NOT value', + 'value ::= TYPECAST value', + 'value ::= variable INCDEC', + 'value ::= HEX', + 'value ::= INTEGER', + 'value ::= INTEGER DOT INTEGER', + 'value ::= INTEGER DOT', + 'value ::= DOT INTEGER', + 'value ::= ID', + 'value ::= function', + 'value ::= OPENP expr CLOSEP', + 'value ::= variable INSTANCEOF ns1', + 'value ::= variable INSTANCEOF variable', + 'value ::= SINGLEQUOTESTRING', + 'value ::= doublequoted_with_quotes', + 'value ::= varindexed DOUBLECOLON static_class_access', + 'value ::= smartytag', + 'value ::= value modifierlist', + 'value ::= NAMESPACE', + 'value ::= arraydef', + 'value ::= ns1 DOUBLECOLON static_class_access', + 'ns1 ::= ID', + 'ns1 ::= NAMESPACE', + 'variable ::= DOLLARID', + 'variable ::= varindexed', + 'variable ::= varvar AT ID', + 'variable ::= object', + 'variable ::= HATCH ID HATCH', + 'variable ::= HATCH ID HATCH arrayindex', + 'variable ::= HATCH variable HATCH', + 'variable ::= HATCH variable HATCH arrayindex', + 'varindexed ::= DOLLARID arrayindex', + 'varindexed ::= varvar arrayindex', + 'arrayindex ::= arrayindex indexdef', + 'arrayindex ::=', + 'indexdef ::= DOT DOLLARID', + 'indexdef ::= DOT varvar', + 'indexdef ::= DOT varvar AT ID', + 'indexdef ::= DOT ID', + 'indexdef ::= DOT INTEGER', + 'indexdef ::= DOT LDEL expr RDEL', + 'indexdef ::= OPENB ID CLOSEB', + 'indexdef ::= OPENB ID DOT ID CLOSEB', + 'indexdef ::= OPENB SINGLEQUOTESTRING CLOSEB', + 'indexdef ::= OPENB INTEGER CLOSEB', + 'indexdef ::= OPENB DOLLARID CLOSEB', + 'indexdef ::= OPENB variable CLOSEB', + 'indexdef ::= OPENB value CLOSEB', + 'indexdef ::= OPENB expr CLOSEB', + 'indexdef ::= OPENB CLOSEB', + 'varvar ::= DOLLARID', + 'varvar ::= DOLLAR', + 'varvar ::= varvar varvarele', + 'varvarele ::= ID', + 'varvarele ::= SIMPELOUTPUT', + 'varvarele ::= LDEL expr RDEL', + 'object ::= varindexed objectchain', + 'objectchain ::= objectelement', + 'objectchain ::= objectchain objectelement', + 'objectelement ::= PTR ID arrayindex', + 'objectelement ::= PTR varvar arrayindex', + 'objectelement ::= PTR LDEL expr RDEL arrayindex', + 'objectelement ::= PTR ID LDEL expr RDEL arrayindex', + 'objectelement ::= PTR method', + 'function ::= ns1 OPENP params CLOSEP', + 'method ::= ID OPENP params CLOSEP', + 'method ::= DOLLARID OPENP params CLOSEP', + 'params ::= params COMMA expr', + 'params ::= expr', + 'params ::=', + 'modifierlist ::= modifierlist modifier modparameters', + 'modifierlist ::= modifier modparameters', + 'modifier ::= VERT AT ID', + 'modifier ::= VERT ID', + 'modparameters ::= modparameters modparameter', + 'modparameters ::=', + 'modparameter ::= COLON value', + 'modparameter ::= COLON UNIMATH value', + 'modparameter ::= COLON array', + 'static_class_access ::= method', + 'static_class_access ::= method objectchain', + 'static_class_access ::= ID', + 'static_class_access ::= DOLLARID arrayindex', + 'static_class_access ::= DOLLARID arrayindex objectchain', + 'lop ::= LOGOP', + 'lop ::= SLOGOP', + 'tlop ::= TLOGOP', + 'scond ::= SINGLECOND', + 'arraydef ::= OPENB arrayelements CLOSEB', + 'arraydef ::= ARRAYOPEN arrayelements CLOSEP', + 'arrayelements ::= arrayelement', + 'arrayelements ::= arrayelements COMMA arrayelement', + 'arrayelements ::=', + 'arrayelement ::= value APTR expr', + 'arrayelement ::= ID APTR expr', + 'arrayelement ::= expr', + 'doublequoted_with_quotes ::= QUOTE QUOTE', + 'doublequoted_with_quotes ::= QUOTE doublequoted QUOTE', + 'doublequoted ::= doublequoted doublequotedcontent', + 'doublequoted ::= doublequotedcontent', + 'doublequotedcontent ::= BACKTICK variable BACKTICK', + 'doublequotedcontent ::= BACKTICK expr BACKTICK', + 'doublequotedcontent ::= DOLLARID', + 'doublequotedcontent ::= LDEL variable RDEL', + 'doublequotedcontent ::= LDEL expr RDEL', + 'doublequotedcontent ::= smartytag', + 'doublequotedcontent ::= TEXT', + ); + + public function tokenName($tokenType) + { + if ($tokenType === 0) { + return 'End of Input'; + } + if ($tokenType > 0 && $tokenType < count($this->yyTokenName)) { + return $this->yyTokenName[$tokenType]; + } else { + return 'Unknown'; + } + } + + public static function yy_destructor($yymajor, $yypminor) + { + switch ($yymajor) { + default: break; /* If no destructor action specified: do nothing */ + } + } + + public function yy_pop_parser_stack() + { + if (empty($this->yystack)) { + return; + } + $yytos = array_pop($this->yystack); + if ($this->yyTraceFILE && $this->yyidx >= 0) { + fwrite($this->yyTraceFILE, + $this->yyTracePrompt . 'Popping ' . $this->yyTokenName[$yytos->major] . + "\n"); + } + $yymajor = $yytos->major; + self::yy_destructor($yymajor, $yytos->minor); + $this->yyidx--; + + return $yymajor; + } + + public function __destruct() + { + while ($this->yystack !== Array()) { + $this->yy_pop_parser_stack(); + } + if (is_resource($this->yyTraceFILE)) { + fclose($this->yyTraceFILE); + } + } + + public function yy_get_expected_tokens($token) + { + static $res3 = array(); + static $res4 = array(); + $state = $this->yystack[$this->yyidx]->stateno; + $expected = self::$yyExpectedTokens[$state]; + if (isset($res3[$state][$token])) { + if ($res3[$state][$token]) { + return $expected; + } + } else { + if ($res3[$state][$token] = in_array($token, self::$yyExpectedTokens[$state], true)) { + return $expected; + } + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ === 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return array_unique($expected); + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno][1]; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno][0]); + if (isset(self::$yyExpectedTokens[$nextstate])) { + $expected = array_merge($expected, self::$yyExpectedTokens[$nextstate]); + if (isset($res4[$nextstate][$token])) { + if ($res4[$nextstate][$token]) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); + } + } else { + if ($res4[$nextstate][$token] = in_array($token, self::$yyExpectedTokens[$nextstate], true)) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); + } + } + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno][0]; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate === self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return array_unique($expected); + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return $expected; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + $this->yyidx = $yyidx; + $this->yystack = $stack; + + return array_unique($expected); + } + + public function yy_is_expected_token($token) + { + static $res = array(); + static $res2 = array(); + if ($token === 0) { + return true; // 0 is not part of this + } + $state = $this->yystack[$this->yyidx]->stateno; + if (isset($res[$state][$token])) { + if ($res[$state][$token]) { + return true; + } + } else { + if ($res[$state][$token] = in_array($token, self::$yyExpectedTokens[$state], true)) { + return true; + } + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ === 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return true; + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno][1]; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno][0]); + if (isset($res2[$nextstate][$token])) { + if ($res2[$nextstate][$token]) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + } else { + if ($res2[$nextstate][$token] = (isset(self::$yyExpectedTokens[$nextstate]) && in_array($token, self::$yyExpectedTokens[$nextstate], true))) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno][0]; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate === self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + if (!$token) { + // end of input: this is valid + return true; + } + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return false; + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return true; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + $this->yyidx = $yyidx; + $this->yystack = $stack; + + return true; + } + + public function yy_find_shift_action($iLookAhead) + { + $stateno = $this->yystack[$this->yyidx]->stateno; + + /* if ($this->yyidx < 0) return self::YY_NO_ACTION; */ + if (!isset(self::$yy_shift_ofst[$stateno])) { + // no shift actions + return self::$yy_default[$stateno]; + } + $i = self::$yy_shift_ofst[$stateno]; + if ($i === self::YY_SHIFT_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead === self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback) + && ($iFallback = self::$yyFallback[$iLookAhead]) != 0) { + if ($this->yyTraceFILE) { + fwrite($this->yyTraceFILE, $this->yyTracePrompt . 'FALLBACK ' . + $this->yyTokenName[$iLookAhead] . ' => ' . + $this->yyTokenName[$iFallback] . "\n"); + } + + return $this->yy_find_shift_action($iFallback); + } + + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + public function yy_find_reduce_action($stateno, $iLookAhead) + { + /* $stateno = $this->yystack[$this->yyidx]->stateno; */ + + if (!isset(self::$yy_reduce_ofst[$stateno])) { + return self::$yy_default[$stateno]; + } + $i = self::$yy_reduce_ofst[$stateno]; + if ($i === self::YY_REDUCE_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead === self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + public function yy_shift($yyNewState, $yyMajor, $yypMinor) + { + $this->yyidx++; + if ($this->yyidx >= self::YYSTACKDEPTH) { + $this->yyidx--; + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sStack Overflow!\n", $this->yyTracePrompt); + } + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } +// line 232 "src/Parser/TemplateParser.y" + + $this->internalError = true; + $this->compiler->trigger_template_error('Stack overflow in template parser'); + + return; + } + $yytos = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $yytos->stateno = $yyNewState; + $yytos->major = $yyMajor; + $yytos->minor = $yypMinor; + $this->yystack[] = $yytos; + if ($this->yyTraceFILE && $this->yyidx > 0) { + fprintf($this->yyTraceFILE, "%sShift %d\n", $this->yyTracePrompt, + $yyNewState); + fprintf($this->yyTraceFILE, "%sStack:", $this->yyTracePrompt); + for ($i = 1; $i <= $this->yyidx; $i++) { + fprintf($this->yyTraceFILE, " %s", + $this->yyTokenName[$this->yystack[$i]->major]); + } + fwrite($this->yyTraceFILE,"\n"); + } + } + + public static $yyRuleInfo = array( + array( 0 => 61, 1 => 1 ), + array( 0 => 62, 1 => 2 ), + array( 0 => 62, 1 => 2 ), + array( 0 => 62, 1 => 2 ), + array( 0 => 62, 1 => 4 ), + array( 0 => 63, 1 => 4 ), + array( 0 => 63, 1 => 1 ), + array( 0 => 64, 1 => 2 ), + array( 0 => 64, 1 => 0 ), + array( 0 => 62, 1 => 2 ), + array( 0 => 62, 1 => 0 ), + array( 0 => 65, 1 => 1 ), + array( 0 => 65, 1 => 1 ), + array( 0 => 65, 1 => 1 ), + array( 0 => 65, 1 => 3 ), + array( 0 => 65, 1 => 2 ), + array( 0 => 66, 1 => 1 ), + array( 0 => 66, 1 => 2 ), + array( 0 => 66, 1 => 2 ), + array( 0 => 69, 1 => 2 ), + array( 0 => 68, 1 => 2 ), + array( 0 => 71, 1 => 1 ), + array( 0 => 71, 1 => 1 ), + array( 0 => 71, 1 => 1 ), + array( 0 => 67, 1 => 3 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 67, 1 => 4 ), + array( 0 => 67, 1 => 5 ), + array( 0 => 67, 1 => 6 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 67, 1 => 3 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 67, 1 => 3 ), + array( 0 => 67, 1 => 8 ), + array( 0 => 79, 1 => 2 ), + array( 0 => 79, 1 => 1 ), + array( 0 => 67, 1 => 5 ), + array( 0 => 67, 1 => 7 ), + array( 0 => 67, 1 => 6 ), + array( 0 => 67, 1 => 8 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 67, 1 => 3 ), + array( 0 => 67, 1 => 4 ), + array( 0 => 65, 1 => 1 ), + array( 0 => 67, 1 => 2 ), + array( 0 => 67, 1 => 3 ), + array( 0 => 67, 1 => 4 ), + array( 0 => 67, 1 => 5 ), + array( 0 => 72, 1 => 2 ), + array( 0 => 72, 1 => 1 ), + array( 0 => 72, 1 => 0 ), + array( 0 => 82, 1 => 4 ), + array( 0 => 82, 1 => 2 ), + array( 0 => 82, 1 => 2 ), + array( 0 => 82, 1 => 2 ), + array( 0 => 82, 1 => 2 ), + array( 0 => 82, 1 => 2 ), + array( 0 => 82, 1 => 4 ), + array( 0 => 78, 1 => 1 ), + array( 0 => 78, 1 => 3 ), + array( 0 => 77, 1 => 3 ), + array( 0 => 77, 1 => 3 ), + array( 0 => 77, 1 => 3 ), + array( 0 => 77, 1 => 3 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 1 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 2 ), + array( 0 => 88, 1 => 1 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 75, 1 => 3 ), + array( 0 => 83, 1 => 4 ), + array( 0 => 84, 1 => 5 ), + array( 0 => 84, 1 => 5 ), + array( 0 => 84, 1 => 5 ), + array( 0 => 84, 1 => 4 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 2 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 1 ), + array( 0 => 74, 1 => 3 ), + array( 0 => 91, 1 => 1 ), + array( 0 => 91, 1 => 1 ), + array( 0 => 73, 1 => 1 ), + array( 0 => 73, 1 => 1 ), + array( 0 => 73, 1 => 3 ), + array( 0 => 73, 1 => 1 ), + array( 0 => 73, 1 => 3 ), + array( 0 => 73, 1 => 4 ), + array( 0 => 73, 1 => 3 ), + array( 0 => 73, 1 => 4 ), + array( 0 => 70, 1 => 2 ), + array( 0 => 70, 1 => 2 ), + array( 0 => 96, 1 => 2 ), + array( 0 => 96, 1 => 0 ), + array( 0 => 97, 1 => 2 ), + array( 0 => 97, 1 => 2 ), + array( 0 => 97, 1 => 4 ), + array( 0 => 97, 1 => 2 ), + array( 0 => 97, 1 => 2 ), + array( 0 => 97, 1 => 4 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 5 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 3 ), + array( 0 => 97, 1 => 2 ), + array( 0 => 80, 1 => 1 ), + array( 0 => 80, 1 => 1 ), + array( 0 => 80, 1 => 2 ), + array( 0 => 98, 1 => 1 ), + array( 0 => 98, 1 => 1 ), + array( 0 => 98, 1 => 3 ), + array( 0 => 95, 1 => 2 ), + array( 0 => 99, 1 => 1 ), + array( 0 => 99, 1 => 2 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 5 ), + array( 0 => 100, 1 => 6 ), + array( 0 => 100, 1 => 2 ), + array( 0 => 90, 1 => 4 ), + array( 0 => 101, 1 => 4 ), + array( 0 => 101, 1 => 4 ), + array( 0 => 102, 1 => 3 ), + array( 0 => 102, 1 => 1 ), + array( 0 => 102, 1 => 0 ), + array( 0 => 76, 1 => 3 ), + array( 0 => 76, 1 => 2 ), + array( 0 => 103, 1 => 3 ), + array( 0 => 103, 1 => 2 ), + array( 0 => 81, 1 => 2 ), + array( 0 => 81, 1 => 0 ), + array( 0 => 104, 1 => 2 ), + array( 0 => 104, 1 => 3 ), + array( 0 => 104, 1 => 2 ), + array( 0 => 93, 1 => 1 ), + array( 0 => 93, 1 => 2 ), + array( 0 => 93, 1 => 1 ), + array( 0 => 93, 1 => 2 ), + array( 0 => 93, 1 => 3 ), + array( 0 => 86, 1 => 1 ), + array( 0 => 86, 1 => 1 ), + array( 0 => 85, 1 => 1 ), + array( 0 => 87, 1 => 1 ), + array( 0 => 94, 1 => 3 ), + array( 0 => 94, 1 => 3 ), + array( 0 => 105, 1 => 1 ), + array( 0 => 105, 1 => 3 ), + array( 0 => 105, 1 => 0 ), + array( 0 => 106, 1 => 3 ), + array( 0 => 106, 1 => 3 ), + array( 0 => 106, 1 => 1 ), + array( 0 => 92, 1 => 2 ), + array( 0 => 92, 1 => 3 ), + array( 0 => 107, 1 => 2 ), + array( 0 => 107, 1 => 1 ), + array( 0 => 108, 1 => 3 ), + array( 0 => 108, 1 => 3 ), + array( 0 => 108, 1 => 1 ), + array( 0 => 108, 1 => 3 ), + array( 0 => 108, 1 => 3 ), + array( 0 => 108, 1 => 1 ), + array( 0 => 108, 1 => 1 ), + ); + + public static $yyReduceMap = array( + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + 5 => 5, + 6 => 6, + 21 => 6, + 22 => 6, + 23 => 6, + 35 => 6, + 55 => 6, + 56 => 6, + 64 => 6, + 65 => 6, + 66 => 6, + 83 => 6, + 88 => 6, + 89 => 6, + 94 => 6, + 98 => 6, + 99 => 6, + 103 => 6, + 104 => 6, + 106 => 6, + 111 => 6, + 175 => 6, + 180 => 6, + 7 => 7, + 8 => 8, + 9 => 9, + 11 => 11, + 12 => 12, + 13 => 13, + 14 => 14, + 15 => 15, + 16 => 16, + 17 => 17, + 18 => 18, + 19 => 19, + 20 => 20, + 24 => 24, + 25 => 25, + 26 => 26, + 27 => 27, + 28 => 28, + 29 => 29, + 30 => 30, + 32 => 30, + 31 => 31, + 33 => 33, + 34 => 34, + 36 => 36, + 37 => 37, + 38 => 38, + 39 => 39, + 40 => 40, + 41 => 41, + 42 => 42, + 43 => 43, + 44 => 44, + 45 => 45, + 46 => 46, + 47 => 47, + 48 => 48, + 49 => 49, + 58 => 49, + 153 => 49, + 157 => 49, + 161 => 49, + 163 => 49, + 50 => 50, + 154 => 50, + 160 => 50, + 51 => 51, + 52 => 52, + 53 => 52, + 54 => 54, + 138 => 54, + 57 => 57, + 59 => 59, + 60 => 60, + 61 => 60, + 62 => 62, + 63 => 63, + 67 => 67, + 68 => 68, + 69 => 69, + 70 => 70, + 71 => 70, + 72 => 72, + 73 => 73, + 74 => 74, + 75 => 75, + 76 => 76, + 77 => 77, + 78 => 78, + 79 => 79, + 80 => 80, + 81 => 80, + 82 => 82, + 84 => 84, + 86 => 84, + 87 => 84, + 118 => 84, + 85 => 85, + 90 => 90, + 91 => 91, + 92 => 92, + 93 => 93, + 95 => 95, + 96 => 96, + 97 => 96, + 100 => 100, + 101 => 101, + 102 => 102, + 105 => 105, + 107 => 107, + 108 => 108, + 109 => 109, + 110 => 110, + 112 => 112, + 113 => 113, + 114 => 114, + 115 => 115, + 116 => 116, + 117 => 117, + 119 => 119, + 177 => 119, + 120 => 120, + 121 => 121, + 122 => 122, + 123 => 123, + 124 => 124, + 125 => 125, + 133 => 125, + 126 => 126, + 127 => 127, + 128 => 128, + 129 => 128, + 131 => 128, + 132 => 128, + 130 => 130, + 134 => 134, + 135 => 135, + 136 => 136, + 181 => 136, + 137 => 137, + 139 => 139, + 140 => 140, + 141 => 141, + 142 => 142, + 143 => 143, + 144 => 144, + 145 => 145, + 146 => 146, + 147 => 147, + 148 => 148, + 149 => 149, + 150 => 150, + 151 => 151, + 152 => 152, + 155 => 155, + 156 => 156, + 158 => 158, + 159 => 159, + 162 => 162, + 164 => 164, + 165 => 165, + 166 => 166, + 167 => 167, + 168 => 168, + 169 => 169, + 170 => 170, + 171 => 171, + 172 => 172, + 173 => 173, + 174 => 173, + 176 => 176, + 178 => 178, + 179 => 179, + 182 => 182, + 183 => 183, + 184 => 184, + 185 => 185, + 188 => 185, + 186 => 186, + 189 => 186, + 187 => 187, + 190 => 190, + 191 => 191, + ); +// line 245 "src/Parser/TemplateParser.y" + public function yy_r0(){ + $this->root_buffer->prepend_array($this, $this->template_prefix); + $this->root_buffer->append_array($this, $this->template_postfix); + $this->_retvalue = $this->root_buffer->to_smarty_php($this); + } +// line 252 "src/Parser/TemplateParser.y" + public function yy_r1(){ + $text = $this->yystack[ $this->yyidx + 0 ]->minor; + + if ((string)$text == '') { + $this->current_buffer->append_subtree($this, null); + } + + $this->current_buffer->append_subtree($this, new \Smarty\ParseTree\Text($text, $this->strip)); + } +// line 262 "src/Parser/TemplateParser.y" + public function yy_r2(){ + $this->strip = true; + } +// line 266 "src/Parser/TemplateParser.y" + public function yy_r3(){ + $this->strip = false; + } +// line 271 "src/Parser/TemplateParser.y" + public function yy_r4(){ + $this->current_buffer->append_subtree($this, new \Smarty\ParseTree\Text($this->yystack[$this->yyidx + -1]->minor)); + } +// line 276 "src/Parser/TemplateParser.y" + public function yy_r5(){ + $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor.$this->yystack[$this->yyidx + -1]->minor; + } +// line 279 "src/Parser/TemplateParser.y" + public function yy_r6(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +// line 283 "src/Parser/TemplateParser.y" + public function yy_r7(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + + } +// line 288 "src/Parser/TemplateParser.y" + public function yy_r8(){ + $this->_retvalue = ''; + } +// line 292 "src/Parser/TemplateParser.y" + public function yy_r9(){ + $this->current_buffer->append_subtree($this, $this->mergePrefixCode($this->yystack[$this->yyidx + 0]->minor)); + $this->compiler->has_variable_string = false; + $this->block_nesting_level = $this->compiler->getTagStackCount(); + } +// line 302 "src/Parser/TemplateParser.y" + public function yy_r11(){ + $var = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + $attributes = []; + if (preg_match('/^(.*)(\s+nocache)$/', $var, $match)) { + $attributes[] = 'nocache'; + $var = $match[1]; + } + $this->_retvalue = $this->compiler->compilePrintExpression($this->compiler->compileVariable('\''.$var.'\''), $attributes); + } +// line 313 "src/Parser/TemplateParser.y" + public function yy_r12(){ + $tag = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength())); + if ($tag == 'strip') { + $this->strip = true; + $this->_retvalue = null; + } else { + if (defined($tag)) { + if ($this->security) { + $this->security->isTrustedConstant($tag, $this->compiler); + } + $this->_retvalue = $this->compiler->compilePrintExpression($tag); + } else { + if (preg_match('/^(.*)(\s+nocache)$/', $tag, $match)) { + $this->_retvalue = $this->compiler->compileTag($match[1],array('\'nocache\'')); + } else { + $this->_retvalue = $this->compiler->compileTag($tag,array()); + } + } + } + } +// line 334 "src/Parser/TemplateParser.y" + public function yy_r13(){ + $j = strrpos($this->yystack[$this->yyidx + 0]->minor,'.'); + if ($this->yystack[$this->yyidx + 0]->minor[$j+1] == 'c') { + // {$smarty.block.child} + $this->_retvalue = $this->compiler->compileChildBlock(); + } else { + // {$smarty.block.parent} + $this->_retvalue = $this->compiler->compileParentBlock(); + } + } +// line 345 "src/Parser/TemplateParser.y" + public function yy_r14(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 349 "src/Parser/TemplateParser.y" + public function yy_r15(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 353 "src/Parser/TemplateParser.y" + public function yy_r16(){ + $this->_retvalue = $this->compiler->compilePrintExpression($this->yystack[$this->yyidx + 0]->minor[0], $this->yystack[$this->yyidx + 0]->minor[1]); + } +// line 362 "src/Parser/TemplateParser.y" + public function yy_r17(){ + $this->_retvalue = $this->compiler->compileTag('assign',array_merge(array(array('value'=>$this->yystack[$this->yyidx + 0]->minor[0]),array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'')),$this->yystack[$this->yyidx + 0]->minor[1])); + } +// line 366 "src/Parser/TemplateParser.y" + public function yy_r18(){ + $this->_retvalue = $this->compiler->compileTag('assign',array_merge(array(array('value'=>$this->yystack[$this->yyidx + 0]->minor[0]),array('var'=>$this->yystack[$this->yyidx + -1]->minor['var'])),$this->yystack[$this->yyidx + 0]->minor[1]),array('smarty_internal_index'=>$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'])); + } +// line 370 "src/Parser/TemplateParser.y" + public function yy_r19(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +// line 374 "src/Parser/TemplateParser.y" + public function yy_r20(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); + } +// line 389 "src/Parser/TemplateParser.y" + public function yy_r24(){ + if (defined($this->yystack[$this->yyidx + -1]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + -1]->minor, $this->compiler); + } + $this->_retvalue = $this->compiler->compilePrintExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); + } else { + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); + } + } +// line 399 "src/Parser/TemplateParser.y" + public function yy_r25(){ + if (defined($this->yystack[$this->yyidx + 0]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + 0]->minor, $this->compiler); + } + $this->_retvalue = $this->compiler->compilePrintExpression($this->yystack[$this->yyidx + 0]->minor); + } else { + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + 0]->minor,array()); + } + } +// line 412 "src/Parser/TemplateParser.y" + public function yy_r26(){ + if (defined($this->yystack[$this->yyidx + -2]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + -2]->minor, $this->compiler); + } + $this->_retvalue = $this->compiler->compilePrintExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + } else { + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -2]->minor,$this->yystack[$this->yyidx + 0]->minor, array('modifierlist'=>$this->yystack[$this->yyidx + -1]->minor)); + } + } +// line 424 "src/Parser/TemplateParser.y" + public function yy_r27(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -3]->minor,$this->yystack[$this->yyidx + 0]->minor,array('object_method'=>$this->yystack[$this->yyidx + -1]->minor)); + } +// line 429 "src/Parser/TemplateParser.y" + public function yy_r28(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -4]->minor,$this->yystack[$this->yyidx + 0]->minor,array('modifierlist'=>$this->yystack[$this->yyidx + -1]->minor, 'object_method'=>$this->yystack[$this->yyidx + -2]->minor)); + } +// line 434 "src/Parser/TemplateParser.y" + public function yy_r29(){ + $tag = trim(substr($this->yystack[$this->yyidx + -1]->minor,$this->compiler->getLdelLength())); + $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 439 "src/Parser/TemplateParser.y" + public function yy_r30(){ + $tag = trim(substr($this->yystack[$this->yyidx + -2]->minor,$this->compiler->getLdelLength())); + $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,$this->yystack[$this->yyidx + 0]->minor,array('if condition'=>$this->yystack[$this->yyidx + -1]->minor)); + } +// line 444 "src/Parser/TemplateParser.y" + public function yy_r31(){ + $tag = trim(substr($this->yystack[$this->yyidx + -1]->minor,$this->compiler->getLdelLength())); + $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 455 "src/Parser/TemplateParser.y" + public function yy_r33(){ + $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -6]->minor),array('ifexp'=>$this->yystack[$this->yyidx + -4]->minor),array('var'=>$this->yystack[$this->yyidx + -2]->minor),array('step'=>$this->yystack[$this->yyidx + -1]->minor))),1); + } +// line 459 "src/Parser/TemplateParser.y" + public function yy_r34(){ + $this->_retvalue = '='.$this->yystack[$this->yyidx + 0]->minor; + } +// line 467 "src/Parser/TemplateParser.y" + public function yy_r36(){ + $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -3]->minor),array('to'=>$this->yystack[$this->yyidx + -1]->minor))),0); + } +// line 471 "src/Parser/TemplateParser.y" + public function yy_r37(){ + $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -5]->minor),array('to'=>$this->yystack[$this->yyidx + -3]->minor),array('step'=>$this->yystack[$this->yyidx + -1]->minor))),0); + } +// line 476 "src/Parser/TemplateParser.y" + public function yy_r38(){ + $this->_retvalue = $this->compiler->compileTag('foreach',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('from'=>$this->yystack[$this->yyidx + -3]->minor),array('item'=>$this->yystack[$this->yyidx + -1]->minor)))); + } +// line 480 "src/Parser/TemplateParser.y" + public function yy_r39(){ + $this->_retvalue = $this->compiler->compileTag('foreach',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('from'=>$this->yystack[$this->yyidx + -5]->minor),array('item'=>$this->yystack[$this->yyidx + -1]->minor),array('key'=>$this->yystack[$this->yyidx + -3]->minor)))); + } +// line 483 "src/Parser/TemplateParser.y" + public function yy_r40(){ + $this->_retvalue = $this->compiler->compileTag('foreach',$this->yystack[$this->yyidx + 0]->minor); + } +// line 488 "src/Parser/TemplateParser.y" + public function yy_r41(){ + $this->_retvalue = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array(array_merge(array($this->yystack[$this->yyidx + -1]->minor),$this->yystack[$this->yyidx + 0]->minor)))); + } +// line 492 "src/Parser/TemplateParser.y" + public function yy_r42(){ + $this->_retvalue = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array_merge(array(array_merge(array($this->yystack[$this->yyidx + -2]->minor),$this->yystack[$this->yyidx + -1]->minor)),$this->yystack[$this->yyidx + 0]->minor))); + } +// line 498 "src/Parser/TemplateParser.y" + public function yy_r43(){ + $tag = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' /'); + if ($tag === 'strip') { + $this->strip = false; + $this->_retvalue = null; + } else { + $this->_retvalue = $this->compiler->compileTag($tag.'close',array()); + } + } +// line 507 "src/Parser/TemplateParser.y" + public function yy_r44(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + 0]->minor.'close',array()); + } +// line 511 "src/Parser/TemplateParser.y" + public function yy_r45(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -1]->minor.'close',array(),array('modifier_list'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 516 "src/Parser/TemplateParser.y" + public function yy_r46(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -2]->minor.'close',array(),array('object_method'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 520 "src/Parser/TemplateParser.y" + public function yy_r47(){ + $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -3]->minor.'close',array(),array('object_method'=>$this->yystack[$this->yyidx + -1]->minor, 'modifier_list'=>$this->yystack[$this->yyidx + 0]->minor)); + } +// line 528 "src/Parser/TemplateParser.y" + public function yy_r48(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + $this->_retvalue[] = $this->yystack[$this->yyidx + 0]->minor; + } +// line 534 "src/Parser/TemplateParser.y" + public function yy_r49(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); + } +// line 539 "src/Parser/TemplateParser.y" + public function yy_r50(){ + $this->_retvalue = array(); + } +// line 544 "src/Parser/TemplateParser.y" + public function yy_r51(){ + if (defined($this->yystack[$this->yyidx + 0]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + 0]->minor, $this->compiler); + } + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>$this->yystack[$this->yyidx + 0]->minor); + } else { + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>'\''.$this->yystack[$this->yyidx + 0]->minor.'\''); + } + } +// line 555 "src/Parser/TemplateParser.y" + public function yy_r52(){ + $this->_retvalue = array(trim($this->yystack[$this->yyidx + -1]->minor," =\n\r\t")=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 563 "src/Parser/TemplateParser.y" + public function yy_r54(){ + $this->_retvalue = '\''.$this->yystack[$this->yyidx + 0]->minor.'\''; + } +// line 575 "src/Parser/TemplateParser.y" + public function yy_r57(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 588 "src/Parser/TemplateParser.y" + public function yy_r59(){ + $this->yystack[$this->yyidx + -2]->minor[]=$this->yystack[$this->yyidx + 0]->minor; + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; + } +// line 593 "src/Parser/TemplateParser.y" + public function yy_r60(){ + $this->_retvalue = array('var' => '\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\'', 'value'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 600 "src/Parser/TemplateParser.y" + public function yy_r62(){ + $this->_retvalue = array('var' => $this->yystack[$this->yyidx + -2]->minor, 'value'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 604 "src/Parser/TemplateParser.y" + public function yy_r63(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 629 "src/Parser/TemplateParser.y" + public function yy_r67(){ + $this->_retvalue = '$_smarty_tpl->getVariable(\''. substr($this->yystack[$this->yyidx + 0]->minor,1) .'\')->preIncDec(\'' . $this->yystack[$this->yyidx + -1]->minor . '\')'; + } +// line 634 "src/Parser/TemplateParser.y" + public function yy_r68(){ + $this->_retvalue = '$_smarty_tpl->getVariable(\''. substr($this->yystack[$this->yyidx + -1]->minor,1) .'\')->postIncDec(\'' . $this->yystack[$this->yyidx + 0]->minor . '\')'; + } +// line 639 "src/Parser/TemplateParser.y" + public function yy_r69(){ + $this->_retvalue = '$_smarty_tpl->getStreamVariable(\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'://' . $this->yystack[$this->yyidx + 0]->minor . '\')'; + } +// line 644 "src/Parser/TemplateParser.y" + public function yy_r70(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor . trim($this->yystack[$this->yyidx + -1]->minor) . $this->yystack[$this->yyidx + 0]->minor; + } +// line 654 "src/Parser/TemplateParser.y" + public function yy_r72(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor['pre']. $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor['op'].$this->yystack[$this->yyidx + 0]->minor .')'; + } +// line 658 "src/Parser/TemplateParser.y" + public function yy_r73(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + } +// line 662 "src/Parser/TemplateParser.y" + public function yy_r74(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor . $this->yystack[$this->yyidx + -1]->minor . ')'; + } +// line 666 "src/Parser/TemplateParser.y" + public function yy_r75(){ + static $isin = [ + 'isin' => 'in_array(', + 'isnotin' => '!in_array(', + ]; + $op = strtolower(str_replace(' ', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $isin[$op]; + } +// line 675 "src/Parser/TemplateParser.y" + public function yy_r76(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor . $this->yystack[$this->yyidx + -2]->minor.','.$this->yystack[$this->yyidx + 0]->minor.')'; + } +// line 679 "src/Parser/TemplateParser.y" + public function yy_r77(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor . $this->yystack[$this->yyidx + -2]->minor.',(array)'.$this->yystack[$this->yyidx + 0]->minor.')'; + } +// line 684 "src/Parser/TemplateParser.y" + public function yy_r78(){ + $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor.' ?? '.$this->yystack[$this->yyidx + 0]->minor; + } +// line 691 "src/Parser/TemplateParser.y" + public function yy_r79(){ + $this->_retvalue = $this->yystack[$this->yyidx + -4]->minor.' ? '. $this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\'') . ' : '.$this->yystack[$this->yyidx + 0]->minor; + } +// line 695 "src/Parser/TemplateParser.y" + public function yy_r80(){ + $this->_retvalue = $this->yystack[$this->yyidx + -4]->minor.' ? '.$this->yystack[$this->yyidx + -2]->minor.' : '.$this->yystack[$this->yyidx + 0]->minor; + } +// line 704 "src/Parser/TemplateParser.y" + public function yy_r82(){ + $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor.' ?: '.$this->yystack[$this->yyidx + 0]->minor; + } +// line 714 "src/Parser/TemplateParser.y" + public function yy_r84(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + } +// line 719 "src/Parser/TemplateParser.y" + public function yy_r85(){ + $this->_retvalue = '!'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 740 "src/Parser/TemplateParser.y" + public function yy_r90(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'.'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 744 "src/Parser/TemplateParser.y" + public function yy_r91(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.'.'; + } +// line 748 "src/Parser/TemplateParser.y" + public function yy_r92(){ + $this->_retvalue = '.'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 753 "src/Parser/TemplateParser.y" + public function yy_r93(){ + if (defined($this->yystack[$this->yyidx + 0]->minor)) { + if ($this->security) { + $this->security->isTrustedConstant($this->yystack[$this->yyidx + 0]->minor, $this->compiler); + } + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } else { + $this->_retvalue = '\''.$this->yystack[$this->yyidx + 0]->minor.'\''; + } + } +// line 770 "src/Parser/TemplateParser.y" + public function yy_r95(){ + $this->_retvalue = '('. $this->yystack[$this->yyidx + -1]->minor .')'; + } +// line 774 "src/Parser/TemplateParser.y" + public function yy_r96(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + } +// line 792 "src/Parser/TemplateParser.y" + public function yy_r100(){ + if ($this->security && $this->security->static_classes !== array()) { + $this->compiler->trigger_template_error('dynamic static class not allowed by security setting'); + } + $prefixVar = $this->compiler->getNewPrefixVariable(); + if ($this->yystack[$this->yyidx + -2]->minor['var'] === '\'smarty\'') { + $this->compiler->appendPrefixCode("compile(array(),$this->compiler,$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index']).';?>'); + } else { + $this->compiler->appendPrefixCode("compiler->compileVariable($this->yystack[$this->yyidx + -2]->minor['var']).$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index'].';?>'); + } + $this->_retvalue = $prefixVar .'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; + } +// line 806 "src/Parser/TemplateParser.y" + public function yy_r101(){ + $prefixVar = $this->compiler->getNewPrefixVariable(); + $tmp = $this->compiler->appendCode('', (string) $this->yystack[$this->yyidx + 0]->minor); + $this->compiler->appendPrefixCode($this->compiler->appendCode($tmp, "")); + $this->_retvalue = $prefixVar; + } +// line 813 "src/Parser/TemplateParser.y" + public function yy_r102(){ + $this->_retvalue = $this->compiler->compileModifier($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + } +// line 826 "src/Parser/TemplateParser.y" + public function yy_r105(){ + if (!in_array(strtolower($this->yystack[$this->yyidx + -2]->minor), array('self', 'parent')) && (!$this->security || $this->security->isTrustedStaticClassAccess($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->compiler))) { + if (isset($this->smarty->registered_classes[$this->yystack[$this->yyidx + -2]->minor])) { + $this->_retvalue = $this->smarty->registered_classes[$this->yystack[$this->yyidx + -2]->minor].'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; + } else { + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; + } + } else { + $this->compiler->trigger_template_error ('static class \''.$this->yystack[$this->yyidx + -2]->minor.'\' is undefined or not allowed by security setting'); + } + } +// line 845 "src/Parser/TemplateParser.y" + public function yy_r107(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +// line 856 "src/Parser/TemplateParser.y" + public function yy_r108(){ + $this->_retvalue = $this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''); + } +// line 859 "src/Parser/TemplateParser.y" + public function yy_r109(){ + if ($this->yystack[$this->yyidx + 0]->minor['var'] === '\'smarty\'') { + $smarty_var = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']); + $this->_retvalue = $smarty_var; + } else { + // used for array reset,next,prev,end,current + $this->last_variable = $this->yystack[$this->yyidx + 0]->minor['var']; + $this->last_index = $this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']; + $this->_retvalue = $this->compiler->compileVariable($this->yystack[$this->yyidx + 0]->minor['var']).$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']; + } + } +// line 872 "src/Parser/TemplateParser.y" + public function yy_r110(){ + $this->_retvalue = '$_smarty_tpl->getVariable('. $this->yystack[$this->yyidx + -2]->minor .')->'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 882 "src/Parser/TemplateParser.y" + public function yy_r112(){ + $this->_retvalue = $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -1]->minor . '\''); + } +// line 886 "src/Parser/TemplateParser.y" + public function yy_r113(){ + $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -2]->minor . '\'') . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' :null)'; + } +// line 890 "src/Parser/TemplateParser.y" + public function yy_r114(){ + $this->_retvalue = $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -1]->minor); + } +// line 894 "src/Parser/TemplateParser.y" + public function yy_r115(){ + $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -2]->minor) . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' : null)'; + } +// line 898 "src/Parser/TemplateParser.y" + public function yy_r116(){ + $this->_retvalue = array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'', 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 901 "src/Parser/TemplateParser.y" + public function yy_r117(){ + $this->_retvalue = array('var'=>$this->yystack[$this->yyidx + -1]->minor, 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 914 "src/Parser/TemplateParser.y" + public function yy_r119(){ + return; + } +// line 920 "src/Parser/TemplateParser.y" + public function yy_r120(){ + $this->_retvalue = '['.$this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\'').']'; + } +// line 923 "src/Parser/TemplateParser.y" + public function yy_r121(){ + $this->_retvalue = '['.$this->compiler->compileVariable($this->yystack[$this->yyidx + 0]->minor).']'; + } +// line 927 "src/Parser/TemplateParser.y" + public function yy_r122(){ + $this->_retvalue = '['.$this->compiler->compileVariable($this->yystack[$this->yyidx + -2]->minor).'->'.$this->yystack[$this->yyidx + 0]->minor.']'; + } +// line 931 "src/Parser/TemplateParser.y" + public function yy_r123(){ + $this->_retvalue = '[\''. $this->yystack[$this->yyidx + 0]->minor .'\']'; + } +// line 935 "src/Parser/TemplateParser.y" + public function yy_r124(){ + $this->_retvalue = '['. $this->yystack[$this->yyidx + 0]->minor .']'; + } +// line 940 "src/Parser/TemplateParser.y" + public function yy_r125(){ + $this->_retvalue = '['. $this->yystack[$this->yyidx + -1]->minor .']'; + } +// line 945 "src/Parser/TemplateParser.y" + public function yy_r126(){ + $this->_retvalue = '['.(new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,'[\'section\'][\''.$this->yystack[$this->yyidx + -1]->minor.'\'][\'index\']').']'; + } +// line 949 "src/Parser/TemplateParser.y" + public function yy_r127(){ + $this->_retvalue = '['.(new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,'[\'section\'][\''.$this->yystack[$this->yyidx + -3]->minor.'\'][\''.$this->yystack[$this->yyidx + -1]->minor.'\']').']'; + } +// line 952 "src/Parser/TemplateParser.y" + public function yy_r128(){ + $this->_retvalue = '['.$this->yystack[$this->yyidx + -1]->minor.']'; + } +// line 958 "src/Parser/TemplateParser.y" + public function yy_r130(){ + $this->_retvalue = '['.$this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'').']'; + } +// line 974 "src/Parser/TemplateParser.y" + public function yy_r134(){ + $this->_retvalue = '[]'; + } +// line 984 "src/Parser/TemplateParser.y" + public function yy_r135(){ + $this->_retvalue = '\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''; + } +// line 988 "src/Parser/TemplateParser.y" + public function yy_r136(){ + $this->_retvalue = '\'\''; + } +// line 993 "src/Parser/TemplateParser.y" + public function yy_r137(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.'.'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1001 "src/Parser/TemplateParser.y" + public function yy_r139(){ + $var = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + $this->_retvalue = $this->compiler->compileVariable('\''.$var.'\''); + } +// line 1007 "src/Parser/TemplateParser.y" + public function yy_r140(){ + $this->_retvalue = '('.$this->yystack[$this->yyidx + -1]->minor.')'; + } +// line 1014 "src/Parser/TemplateParser.y" + public function yy_r141(){ + if ($this->yystack[$this->yyidx + -1]->minor['var'] === '\'smarty\'') { + $this->_retvalue = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index']).$this->yystack[$this->yyidx + 0]->minor; + } else { + $this->_retvalue = $this->compiler->compileVariable($this->yystack[$this->yyidx + -1]->minor['var']).$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'].$this->yystack[$this->yyidx + 0]->minor; + } + } +// line 1023 "src/Parser/TemplateParser.y" + public function yy_r142(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +// line 1028 "src/Parser/TemplateParser.y" + public function yy_r143(){ + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1033 "src/Parser/TemplateParser.y" + public function yy_r144(){ + if ($this->security && substr($this->yystack[$this->yyidx + -1]->minor,0,1) === '_') { + $this->compiler->trigger_template_error (self::ERR1); + } + $this->_retvalue = '->'.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1040 "src/Parser/TemplateParser.y" + public function yy_r145(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $this->_retvalue = '->{'.$this->compiler->compileVariable($this->yystack[$this->yyidx + -1]->minor).$this->yystack[$this->yyidx + 0]->minor.'}'; + } +// line 1047 "src/Parser/TemplateParser.y" + public function yy_r146(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $this->_retvalue = '->{'.$this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + 0]->minor.'}'; + } +// line 1054 "src/Parser/TemplateParser.y" + public function yy_r147(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $this->_retvalue = '->{\''.$this->yystack[$this->yyidx + -4]->minor.'\'.'.$this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + 0]->minor.'}'; + } +// line 1062 "src/Parser/TemplateParser.y" + public function yy_r148(){ + $this->_retvalue = '->'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1070 "src/Parser/TemplateParser.y" + public function yy_r149(){ + $this->_retvalue = $this->compiler->compileModifierInExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); + } +// line 1078 "src/Parser/TemplateParser.y" + public function yy_r150(){ + if ($this->security && substr($this->yystack[$this->yyidx + -3]->minor,0,1) === '_') { + $this->compiler->trigger_template_error (self::ERR1); + } + $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor . '('. implode(',',$this->yystack[$this->yyidx + -1]->minor) .')'; + } +// line 1085 "src/Parser/TemplateParser.y" + public function yy_r151(){ + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $prefixVar = $this->compiler->getNewPrefixVariable(); + $this->compiler->appendPrefixCode("compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -3]->minor,1).'\'').';?>'); + $this->_retvalue = $prefixVar .'('. implode(',',$this->yystack[$this->yyidx + -1]->minor) .')'; + } +// line 1096 "src/Parser/TemplateParser.y" + public function yy_r152(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array($this->yystack[$this->yyidx + 0]->minor)); + } +// line 1113 "src/Parser/TemplateParser.y" + public function yy_r155(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array(array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor))); + } +// line 1117 "src/Parser/TemplateParser.y" + public function yy_r156(){ + $this->_retvalue = array(array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor)); + } +// line 1125 "src/Parser/TemplateParser.y" + public function yy_r158(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); + } +// line 1133 "src/Parser/TemplateParser.y" + public function yy_r159(){ + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); + } +// line 1146 "src/Parser/TemplateParser.y" + public function yy_r162(){ + $this->_retvalue = array(trim($this->yystack[$this->yyidx + -1]->minor).$this->yystack[$this->yyidx + 0]->minor); + } +// line 1155 "src/Parser/TemplateParser.y" + public function yy_r164(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor, '', 'method'); + } +// line 1160 "src/Parser/TemplateParser.y" + public function yy_r165(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, 'method'); + } +// line 1165 "src/Parser/TemplateParser.y" + public function yy_r166(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor, ''); + } +// line 1170 "src/Parser/TemplateParser.y" + public function yy_r167(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, 'property'); + } +// line 1175 "src/Parser/TemplateParser.y" + public function yy_r168(){ + $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor, 'property'); + } +// line 1181 "src/Parser/TemplateParser.y" + public function yy_r169(){ + $this->_retvalue = ' '. trim($this->yystack[$this->yyidx + 0]->minor) . ' '; + } +// line 1185 "src/Parser/TemplateParser.y" + public function yy_r170(){ + static $lops = array( + 'eq' => ' == ', + 'ne' => ' != ', + 'neq' => ' != ', + 'gt' => ' > ', + 'ge' => ' >= ', + 'gte' => ' >= ', + 'lt' => ' < ', + 'le' => ' <= ', + 'lte' => ' <= ', + 'mod' => ' % ', + 'and' => ' && ', + 'or' => ' || ', + 'xor' => ' xor ', + ); + $op = strtolower(preg_replace('/\s*/', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $lops[$op]; + } +// line 1204 "src/Parser/TemplateParser.y" + public function yy_r171(){ + static $tlops = array( + 'isdivby' => array('op' => ' % ', 'pre' => '!('), + 'isnotdivby' => array('op' => ' % ', 'pre' => '('), + 'isevenby' => array('op' => ' / ', 'pre' => '!(1 & '), + 'isnotevenby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isoddby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isnotoddby' => array('op' => ' / ', 'pre' => '!(1 & '), + ); + $op = strtolower(preg_replace('/\s*/', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $tlops[$op]; + } +// line 1217 "src/Parser/TemplateParser.y" + public function yy_r172(){ + static $scond = array ( + 'iseven' => '!(1 & ', + 'isnoteven' => '(1 & ', + 'isodd' => '(1 & ', + 'isnotodd' => '!(1 & ', + ); + $op = strtolower(str_replace(' ', '', $this->yystack[$this->yyidx + 0]->minor)); + $this->_retvalue = $scond[$op]; + } +// line 1231 "src/Parser/TemplateParser.y" + public function yy_r173(){ + $this->_retvalue = 'array('.$this->yystack[$this->yyidx + -1]->minor.')'; + } +// line 1242 "src/Parser/TemplateParser.y" + public function yy_r176(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.','.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1250 "src/Parser/TemplateParser.y" + public function yy_r178(){ + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'=>'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1254 "src/Parser/TemplateParser.y" + public function yy_r179(){ + $this->_retvalue = '\''.$this->yystack[$this->yyidx + -2]->minor.'\'=>'.$this->yystack[$this->yyidx + 0]->minor; + } +// line 1270 "src/Parser/TemplateParser.y" + public function yy_r182(){ + $this->compiler->leaveDoubleQuote(); + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor->to_smarty_php($this); + } +// line 1276 "src/Parser/TemplateParser.y" + public function yy_r183(){ + $this->yystack[$this->yyidx + -1]->minor->append_subtree($this, $this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; + } +// line 1281 "src/Parser/TemplateParser.y" + public function yy_r184(){ + $this->_retvalue = new Dq($this, $this->yystack[$this->yyidx + 0]->minor); + } +// line 1285 "src/Parser/TemplateParser.y" + public function yy_r185(){ + $this->_retvalue = new Code('(string)'.$this->yystack[$this->yyidx + -1]->minor); + } +// line 1289 "src/Parser/TemplateParser.y" + public function yy_r186(){ + $this->_retvalue = new Code('(string)('.$this->yystack[$this->yyidx + -1]->minor.')'); + } +// line 1293 "src/Parser/TemplateParser.y" + public function yy_r187(){ + $this->_retvalue = new Code('(string)$_smarty_tpl->getValue(\''. substr($this->yystack[$this->yyidx + 0]->minor,1) .'\')'); + } +// line 1305 "src/Parser/TemplateParser.y" + public function yy_r190(){ + $this->_retvalue = new Tag($this, $this->yystack[$this->yyidx + 0]->minor); + } +// line 1309 "src/Parser/TemplateParser.y" + public function yy_r191(){ + $this->_retvalue = new DqContent($this->yystack[$this->yyidx + 0]->minor); + } + + private $_retvalue; + + public function yy_reduce($yyruleno) + { + if ($this->yyTraceFILE && $yyruleno >= 0 + && $yyruleno < count(self::$yyRuleName)) { + fprintf($this->yyTraceFILE, "%sReduce (%d) [%s].\n", + $this->yyTracePrompt, $yyruleno, + self::$yyRuleName[$yyruleno]); + } + + $this->_retvalue = $yy_lefthand_side = null; + if (isset(self::$yyReduceMap[$yyruleno])) { + // call the action + $this->_retvalue = null; + $this->{'yy_r' . self::$yyReduceMap[$yyruleno]}(); + $yy_lefthand_side = $this->_retvalue; + } + $yygoto = self::$yyRuleInfo[$yyruleno][0]; + $yysize = self::$yyRuleInfo[$yyruleno][1]; + $this->yyidx -= $yysize; + for ($i = $yysize; $i; $i--) { + // pop all of the right-hand side parameters + array_pop($this->yystack); + } + $yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto); + if ($yyact < self::YYNSTATE) { + if (!$this->yyTraceFILE && $yysize) { + $this->yyidx++; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = $yyact; + $x->major = $yygoto; + $x->minor = $yy_lefthand_side; + $this->yystack[$this->yyidx] = $x; + } else { + $this->yy_shift($yyact, $yygoto, $yy_lefthand_side); + } + } elseif ($yyact === self::YYNSTATE + self::YYNRULE + 1) { + $this->yy_accept(); + } + } + + public function yy_parse_failed() + { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sFail!\n", $this->yyTracePrompt); + } while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + } + + public function yy_syntax_error($yymajor, $TOKEN) + { +// line 225 "src/Parser/TemplateParser.y" + + $this->internalError = true; + $this->yymajor = $yymajor; + $this->compiler->trigger_template_error(); + } + + public function yy_accept() + { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sAccept!\n", $this->yyTracePrompt); + } while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } +// line 218 "src/Parser/TemplateParser.y" + + $this->successful = !$this->internalError; + $this->internalError = false; + $this->retvalue = $this->_retvalue; + } + + public function doParse($yymajor, $yytokenvalue) + { + $yyerrorhit = 0; /* True if yymajor has invoked an error */ + + if ($this->yyidx === null || $this->yyidx < 0) { + $this->yyidx = 0; + $this->yyerrcnt = -1; + $x = (object) ['stateno' => null, 'major' => null, 'minor' => null]; + $x->stateno = 0; + $x->major = 0; + $this->yystack = array(); + $this->yystack[] = $x; + } + $yyendofinput = ($yymajor==0); + + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sInput %s\n", + $this->yyTracePrompt, $this->yyTokenName[$yymajor]); + } + + do { + $yyact = $this->yy_find_shift_action($yymajor); + if ($yymajor < self::YYERRORSYMBOL && + !$this->yy_is_expected_token($yymajor)) { + // force a syntax error + $yyact = self::YY_ERROR_ACTION; + } + if ($yyact < self::YYNSTATE) { + $this->yy_shift($yyact, $yymajor, $yytokenvalue); + $this->yyerrcnt--; + if ($yyendofinput && $this->yyidx >= 0) { + $yymajor = 0; + } else { + $yymajor = self::YYNOCODE; + } + } elseif ($yyact < self::YYNSTATE + self::YYNRULE) { + $this->yy_reduce($yyact - self::YYNSTATE); + } elseif ($yyact === self::YY_ERROR_ACTION) { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sSyntax Error!\n", + $this->yyTracePrompt); + } + if (self::YYERRORSYMBOL) { + if ($this->yyerrcnt < 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $yymx = $this->yystack[$this->yyidx]->major; + if ($yymx === self::YYERRORSYMBOL || $yyerrorhit) { + if ($this->yyTraceFILE) { + fprintf($this->yyTraceFILE, "%sDiscard input token %s\n", + $this->yyTracePrompt, $this->yyTokenName[$yymajor]); + } + $this->yy_destructor($yymajor, $yytokenvalue); + $yymajor = self::YYNOCODE; + } else { + while ($this->yyidx >= 0 && + $yymx !== self::YYERRORSYMBOL && + ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE + ){ + $this->yy_pop_parser_stack(); + } + if ($this->yyidx < 0 || $yymajor==0) { + $this->yy_destructor($yymajor, $yytokenvalue); + $this->yy_parse_failed(); + $yymajor = self::YYNOCODE; + } elseif ($yymx !== self::YYERRORSYMBOL) { + $u2 = 0; + $this->yy_shift($yyact, self::YYERRORSYMBOL, $u2); + } + } + $this->yyerrcnt = 3; + $yyerrorhit = 1; + } else { + if ($this->yyerrcnt <= 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $this->yyerrcnt = 3; + $this->yy_destructor($yymajor, $yytokenvalue); + if ($yyendofinput) { + $this->yy_parse_failed(); + } + $yymajor = self::YYNOCODE; + } + } else { + $this->yy_accept(); + $yymajor = self::YYNOCODE; + } + } while ($yymajor !== self::YYNOCODE && $this->yyidx >= 0); + } +} + diff --git a/src/Parser/TemplateParser.y b/src/Parser/TemplateParser.y new file mode 100644 index 0000000..f1e3c35 --- /dev/null +++ b/src/Parser/TemplateParser.y @@ -0,0 +1,1312 @@ +/* + * This file is part of Smarty. + * + * (c) 2015 Uwe Tews + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +%stack_size 500 +%name TP_ +%declare_class { + +namespace Smarty\Parser; + +use \Smarty\Lexer\TemplateLexer as Lexer; +use \Smarty\ParseTree\Template as TemplateParseTree; +use \Smarty\Compiler\Template as TemplateCompiler; +use \Smarty\ParseTree\Code; +use \Smarty\ParseTree\Dq; +use \Smarty\ParseTree\DqContent; +use \Smarty\ParseTree\Tag; + + +/** +* Smarty Template Parser Class +* +* This is the template parser. +* It is generated from the TemplateParser.y file +* +* @author Uwe Tews +*/ +class TemplateParser +} +%include_class +{ + const ERR1 = 'Security error: Call to private object member not allowed'; + const ERR2 = 'Security error: Call to dynamic object member not allowed'; + + /** + * result status + * + * @var bool + */ + public $successful = true; + + /** + * return value + * + * @var mixed + */ + public $retvalue = 0; + + /** + * @var + */ + public $yymajor; + + /** + * last index of array variable + * + * @var mixed + */ + public $last_index; + + /** + * last variable name + * + * @var string + */ + public $last_variable; + + /** + * root parse tree buffer + * + * @var TemplateParseTree + */ + public $root_buffer; + + /** + * current parse tree object + * + * @var \Smarty\ParseTree\Base + */ + public $current_buffer; + + /** + * lexer object + * + * @var Lexer + */ + public $lex; + + /** + * internal error flag + * + * @var bool + */ + private $internalError = false; + + /** + * {strip} status + * + * @var bool + */ + public $strip = false; + /** + * compiler object + * + * @var TemplateCompiler + */ + public $compiler = null; + + /** + * smarty object + * + * @var \Smarty\Smarty + */ + public $smarty = null; + + /** + * template object + * + * @var \Smarty\Template + */ + public $template = null; + + /** + * block nesting level + * + * @var int + */ + public $block_nesting_level = 0; + + /** + * security object + * + * @var \Smarty\Security + */ + public $security = null; + + /** + * template prefix array + * + * @var \Smarty\ParseTree\Base[] + */ + public $template_prefix = array(); + + /** + * template prefix array + * + * @var \Smarty\ParseTree\Base[] + */ + public $template_postfix = array(); + + /** + * constructor + * + * @param Lexer $lex + * @param TemplateCompiler $compiler + */ + public function __construct(Lexer $lex, TemplateCompiler $compiler) + { + $this->lex = $lex; + $this->compiler = $compiler; + $this->template = $this->compiler->getTemplate(); + $this->smarty = $this->template->getSmarty(); + $this->security = $this->smarty->security_policy ?? false; + $this->current_buffer = $this->root_buffer = new TemplateParseTree(); + } + + /** + * insert PHP code in current buffer + * + * @param string $code + */ + public function insertPhpCode($code) + { + $this->current_buffer->append_subtree($this, new Tag($this, $code)); + } + + /** + * error rundown + * + */ + public function errorRunDown() + { + while ($this->yystack !== array()) { + $this->yy_pop_parser_stack(); + } + if (is_resource($this->yyTraceFILE)) { + fclose($this->yyTraceFILE); + } + } + + /** + * merge PHP code with prefix code and return parse tree tag object + * + * @param string $code + * + * @return Tag + */ + private function mergePrefixCode($code) + { + $tmp = ''; + foreach ($this->compiler->prefix_code as $preCode) { + $tmp .= $preCode; + } + $this->compiler->prefix_code = array(); + $tmp .= $code; + return new Tag($this, $this->compiler->processNocacheCode($tmp)); + } + +} + +%token_prefix TP_ + +%parse_accept +{ + $this->successful = !$this->internalError; + $this->internalError = false; + $this->retvalue = $this->_retvalue; +} + +%syntax_error +{ + $this->internalError = true; + $this->yymajor = $yymajor; + $this->compiler->trigger_template_error(); +} + +%stack_overflow +{ + $this->internalError = true; + $this->compiler->trigger_template_error('Stack overflow in template parser'); +} + + +%right VERT. +%left COLON. + + + // + // complete template + // +start(res) ::= template. { + $this->root_buffer->prepend_array($this, $this->template_prefix); + $this->root_buffer->append_array($this, $this->template_postfix); + res = $this->root_buffer->to_smarty_php($this); +} + + // template text +template ::= template TEXT(B). { + $text = $this->yystack[ $this->yyidx + 0 ]->minor; + + if ((string)$text == '') { + $this->current_buffer->append_subtree($this, null); + } + + $this->current_buffer->append_subtree($this, new \Smarty\ParseTree\Text($text, $this->strip)); +} + // strip on +template ::= template STRIPON. { + $this->strip = true; +} + // strip off +template ::= template STRIPOFF. { + $this->strip = false; +} + + // Literal +template ::= template LITERALSTART literal_e2(B) LITERALEND. { + $this->current_buffer->append_subtree($this, new \Smarty\ParseTree\Text(B)); +} + + +literal_e2(A) ::= literal_e1(B) LITERALSTART literal_e1(C) LITERALEND. { + A = B.C; +} +literal_e2(A) ::= literal_e1(B). { + A = B; +} + +literal_e1(A) ::= literal_e1(B) LITERAL(C). { + A = B.C; + +} + +literal_e1(A) ::= . { + A = ''; +} + // Smarty tag +template ::= template smartytag(B). { + $this->current_buffer->append_subtree($this, $this->mergePrefixCode(B)); + $this->compiler->has_variable_string = false; + $this->block_nesting_level = $this->compiler->getTagStackCount(); +} + + + // empty template +template ::= . + +smartytag(A) ::= SIMPELOUTPUT(B). { + $var = trim(substr(B, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + $attributes = []; + if (preg_match('/^(.*)(\s+nocache)$/', $var, $match)) { + $attributes[] = 'nocache'; + $var = $match[1]; + } + A = $this->compiler->compilePrintExpression($this->compiler->compileVariable('\''.$var.'\''), $attributes); +} + +// simple tag like {name} +smartytag(A)::= SIMPLETAG(B). { + $tag = trim(substr(B, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength())); + if ($tag == 'strip') { + $this->strip = true; + A = null; + } else { + if (defined($tag)) { + if ($this->security) { + $this->security->isTrustedConstant($tag, $this->compiler); + } + A = $this->compiler->compilePrintExpression($tag); + } else { + if (preg_match('/^(.*)(\s+nocache)$/', $tag, $match)) { + A = $this->compiler->compileTag($match[1],array('\'nocache\'')); + } else { + A = $this->compiler->compileTag($tag,array()); + } + } + } +} + // {$smarty.block.child} or {$smarty.block.parent} +smartytag(A) ::= SMARTYBLOCKCHILDPARENT(i). { + $j = strrpos(i,'.'); + if (i[$j+1] == 'c') { + // {$smarty.block.child} + A = $this->compiler->compileChildBlock(); + } else { + // {$smarty.block.parent} + A = $this->compiler->compileParentBlock(); + } +} + +smartytag(A) ::= LDEL tagbody(B) RDEL. { + A = B; +} + + smartytag(A) ::= tag(B) RDEL. { + A = B; + } + // output with optional attributes +tagbody(A) ::= outattr(B). { + A = $this->compiler->compilePrintExpression(B[0], B[1]); +} + +// +// Smarty tags start here +// + + // assign new style +tagbody(A) ::= DOLLARID(B) eqoutattr(C). { + A = $this->compiler->compileTag('assign',array_merge(array(array('value'=>C[0]),array('var'=>'\''.substr(B,1).'\'')),C[1])); +} + +tagbody(A) ::= varindexed(B) eqoutattr(C). { + A = $this->compiler->compileTag('assign',array_merge(array(array('value'=>C[0]),array('var'=>B['var'])),C[1]),array('smarty_internal_index'=>B['smarty_internal_index'])); +} + +eqoutattr(A) ::= EQUAL outattr(B). { + A = B; +} + +outattr(A) ::= output(B) attributes(C). { + A = array(B,C); +} + +output(A) ::= variable(B). { + A = B; +} +output(A) ::= value(B). { + A = B; +} +output(A) ::= expr(B). { + A = B; +} + + // tag with optional Smarty2 style attributes +tag(res) ::= LDEL ID(i) attributes(a). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = $this->compiler->compilePrintExpression(i, a); + } else { + res = $this->compiler->compileTag(i,a); + } +} +tag(res) ::= LDEL ID(i). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = $this->compiler->compilePrintExpression(i); + } else { + res = $this->compiler->compileTag(i,array()); + } +} + + + // tag with modifier and optional Smarty2 style attributes +tag(res) ::= LDEL ID(i) modifierlist(l)attributes(a). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = $this->compiler->compilePrintExpression(i, a, l); + } else { + res = $this->compiler->compileTag(i,a, array('modifierlist'=>l)); + } +} + + // registered object tag +tag(res) ::= LDEL ID(i) PTR ID(m) attributes(a). { + res = $this->compiler->compileTag(i,a,array('object_method'=>m)); +} + + // registered object tag with modifiers +tag(res) ::= LDEL ID(i) PTR ID(me) modifierlist(l) attributes(a). { + res = $this->compiler->compileTag(i,a,array('modifierlist'=>l, 'object_method'=>me)); +} + + // {if}, {elseif} and {while} tag +tag(res) ::= LDELIF(i) expr(ie). { + $tag = trim(substr(i,$this->compiler->getLdelLength())); + res = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>ie)); +} + +tag(res) ::= LDELIF(i) expr(ie) attributes(a). { + $tag = trim(substr(i,$this->compiler->getLdelLength())); + res = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,a,array('if condition'=>ie)); +} + +tag(res) ::= LDELIF(i) statement(ie). { + $tag = trim(substr(i,$this->compiler->getLdelLength())); + res = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>ie)); +} + +tag(res) ::= LDELIF(i) statement(ie) attributes(a). { + $tag = trim(substr(i,$this->compiler->getLdelLength())); + res = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,a,array('if condition'=>ie)); +} + + // {for} tag +tag(res) ::= LDELFOR statements(st) SEMICOLON expr(ie) SEMICOLON varindexed(v2) foraction(e2) attributes(a). { + res = $this->compiler->compileTag('for',array_merge(a,array(array('start'=>st),array('ifexp'=>ie),array('var'=>v2),array('step'=>e2))),1); +} + + foraction(res) ::= EQUAL expr(e). { + res = '='.e; +} + + foraction(res) ::= INCDEC(e). { + res = e; +} + +tag(res) ::= LDELFOR statement(st) TO expr(v) attributes(a). { + res = $this->compiler->compileTag('for',array_merge(a,array(array('start'=>st),array('to'=>v))),0); +} + +tag(res) ::= LDELFOR statement(st) TO expr(v) STEP expr(v2) attributes(a). { + res = $this->compiler->compileTag('for',array_merge(a,array(array('start'=>st),array('to'=>v),array('step'=>v2))),0); +} + + // {foreach} tag +tag(res) ::= LDELFOREACH SPACE expr(e) AS varvar(v0) attributes(a). { + res = $this->compiler->compileTag('foreach',array_merge(a,array(array('from'=>e),array('item'=>v0)))); +} + +tag(res) ::= LDELFOREACH SPACE expr(e) AS varvar(v1) APTR varvar(v0) attributes(a). { + res = $this->compiler->compileTag('foreach',array_merge(a,array(array('from'=>e),array('item'=>v0),array('key'=>v1)))); +} +tag(res) ::= LDELFOREACH attributes(a). { + res = $this->compiler->compileTag('foreach',a); +} + + // {setfilter} +tag(res) ::= LDELSETFILTER ID(m) modparameters(p). { + res = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array(array_merge(array(m),p)))); +} + +tag(res) ::= LDELSETFILTER ID(m) modparameters(p) modifierlist(l). { + res = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array_merge(array(array_merge(array(m),p)),l))); +} + + + // end of block tag {/....} +smartytag(res)::= CLOSETAG(t). { + $tag = trim(substr(t, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' /'); + if ($tag === 'strip') { + $this->strip = false; + res = null; + } else { + res = $this->compiler->compileTag($tag.'close',array()); + } + } +tag(res) ::= LDELSLASH ID(i). { + res = $this->compiler->compileTag(i.'close',array()); +} + +tag(res) ::= LDELSLASH ID(i) modifierlist(l). { + res = $this->compiler->compileTag(i.'close',array(),array('modifier_list'=>l)); +} + + // end of block object tag {/....} +tag(res) ::= LDELSLASH ID(i) PTR ID(m). { + res = $this->compiler->compileTag(i.'close',array(),array('object_method'=>m)); +} + +tag(res) ::= LDELSLASH ID(i) PTR ID(m) modifierlist(l). { + res = $this->compiler->compileTag(i.'close',array(),array('object_method'=>m, 'modifier_list'=>l)); +} + +// +//Attributes of Smarty tags +// + // list of attributes +attributes(res) ::= attributes(a1) attribute(a2). { + res = a1; + res[] = a2; +} + + // single attribute +attributes(res) ::= attribute(a). { + res = array(a); +} + + // no attributes +attributes(res) ::= . { + res = array(); +} + + // attribute +attribute(res) ::= SPACE ID(v) EQUAL ID(id). { + if (defined(id)) { + if ($this->security) { + $this->security->isTrustedConstant(id, $this->compiler); + } + res = array(v=>id); + } else { + res = array(v=>'\''.id.'\''); + } +} + +attribute(res) ::= ATTR(v) expr(e). { + res = array(trim(v," =\n\r\t")=>e); +} + +attribute(res) ::= ATTR(v) value(e). { + res = array(trim(v," =\n\r\t")=>e); +} + +attribute(res) ::= SPACE ID(v). { + res = '\''.v.'\''; +} + +attribute(res) ::= SPACE expr(e). { + res = e; +} + +attribute(res) ::= SPACE value(v). { + res = v; +} + +attribute(res) ::= SPACE INTEGER(i) EQUAL expr(e). { + res = array(i=>e); +} + + + +// +// statement +// +statements(res) ::= statement(s). { + res = array(s); +} + +statements(res) ::= statements(s1) COMMA statement(s). { + s1[]=s; + res = s1; +} + +statement(res) ::= DOLLARID(i) EQUAL INTEGER(e). { + res = array('var' => '\''.substr(i,1).'\'', 'value'=>e); +} +statement(res) ::= DOLLARID(i) EQUAL expr(e). { + res = array('var' => '\''.substr(i,1).'\'', 'value'=>e); +} + +statement(res) ::= varindexed(vi) EQUAL expr(e). { + res = array('var' => vi, 'value'=>e); +} + +statement(res) ::= OPENP statement(st) CLOSEP. { + res = st; +} + + +// +// expressions +// + + // single value +expr(res) ::= value(v). { + res = v; +} + + // nullcoalescing +expr(res) ::= nullcoalescing(v). { + res = v; +} + + // ternary +expr(res) ::= ternary(v). { + res = v; +} + + // ++$a / --$a +expr(res) ::= INCDEC(i2) DOLLARID(i). { + res = '$_smarty_tpl->getVariable(\''. substr(i,1) .'\')->preIncDec(\'' . i2 . '\')'; +} + + // $a++ / $a-- +expr(res) ::= DOLLARID(i) INCDEC(i2). { + res = '$_smarty_tpl->getVariable(\''. substr(i,1) .'\')->postIncDec(\'' . i2 . '\')'; +} + + // resources/streams +expr(res) ::= DOLLARID(i) COLON ID(i2). { + res = '$_smarty_tpl->getStreamVariable(\''.substr(i,1).'://' . i2 . '\')'; +} + + // arithmetic expression +expr(res) ::= expr(e) MATH(m) value(v). { + res = e . trim(m) . v; +} + +expr(res) ::= expr(e) UNIMATH(m) value(v). { + res = e . trim(m) . v; +} + +// if expression + // special conditions +expr(res) ::= expr(e1) tlop(c) value(e2). { + res = c['pre']. e1.c['op'].e2 .')'; +} + // simple expression +expr(res) ::= expr(e1) lop(c) expr(e2). { + res = e1.c.e2; +} + +expr(res) ::= expr(e1) scond(c). { + res = c . e1 . ')'; +} + +isin(res) ::= ISIN(o). { + static $isin = [ + 'isin' => 'in_array(', + 'isnotin' => '!in_array(', + ]; + $op = strtolower(str_replace(' ', '', o)); + res = $isin[$op]; +} + +expr(res) ::= expr(e1) isin(c) array(a). { + res = c . e1.','.a.')'; +} + +expr(res) ::= expr(e1) isin(c) value(v). { + res = c . e1.',(array)'.v.')'; +} + +// null coalescing +nullcoalescing(res) ::= expr(v) QMARK QMARK expr(e2). { + res = v.' ?? '.e2; +} + +// +// ternary +// +ternary(res) ::= expr(v) QMARK DOLLARID(e1) COLON expr(e2). { + res = v.' ? '. $this->compiler->compileVariable('\''.substr(e1,1).'\'') . ' : '.e2; +} + +ternary(res) ::= expr(v) QMARK value(e1) COLON expr(e2). { + res = v.' ? '.e1.' : '.e2; +} + +ternary(res) ::= expr(v) QMARK expr(e1) COLON expr(e2). { + res = v.' ? '.e1.' : '.e2; +} + +// shorthand ternary +ternary(res) ::= expr(v) QMARK COLON expr(e2). { + res = v.' ?: '.e2; +} + + // value +value(res) ::= variable(v). { + res = v; +} + + // +/- value +value(res) ::= UNIMATH(m) value(v). { + res = m.v; +} + + // logical negation +value(res) ::= NOT value(v). { + res = '!'.v; +} + +value(res) ::= TYPECAST(t) value(v). { + res = t.v; +} + +value(res) ::= variable(v) INCDEC(o). { + res = v.o; +} + + // numeric +value(res) ::= HEX(n). { + res = n; +} + +value(res) ::= INTEGER(n). { + res = n; +} + +value(res) ::= INTEGER(n1) DOT INTEGER(n2). { + res = n1.'.'.n2; +} + +value(res) ::= INTEGER(n1) DOT. { + res = n1.'.'; +} + +value(res) ::= DOT INTEGER(n1). { + res = '.'.n1; +} + + // ID, true, false, null +value(res) ::= ID(id). { + if (defined(id)) { + if ($this->security) { + $this->security->isTrustedConstant(id, $this->compiler); + } + res = id; + } else { + res = '\''.id.'\''; + } +} + + // function call +value(res) ::= function(f). { + res = f; +} + + // expression +value(res) ::= OPENP expr(e) CLOSEP. { + res = '('. e .')'; +} + +value(res) ::= variable(v1) INSTANCEOF(i) ns1(v2). { + res = v1.i.v2; +} +value(res) ::= variable(v1) INSTANCEOF(i) variable(v2). { + res = v1.i.v2; +} + + // singele quoted string +value(res) ::= SINGLEQUOTESTRING(t). { + res = t; +} + + // double quoted string +value(res) ::= doublequoted_with_quotes(s). { + res = s; +} + + +value(res) ::= varindexed(vi) DOUBLECOLON static_class_access(r). { + if ($this->security && $this->security->static_classes !== array()) { + $this->compiler->trigger_template_error('dynamic static class not allowed by security setting'); + } + $prefixVar = $this->compiler->getNewPrefixVariable(); + if (vi['var'] === '\'smarty\'') { + $this->compiler->appendPrefixCode("compile(array(),$this->compiler,vi['smarty_internal_index']).';?>'); + } else { + $this->compiler->appendPrefixCode("compiler->compileVariable(vi['var']).vi['smarty_internal_index'].';?>'); + } + res = $prefixVar .'::'.r[0].r[1]; +} + + // Smarty tag +value(res) ::= smartytag(st). { + $prefixVar = $this->compiler->getNewPrefixVariable(); + $tmp = $this->compiler->appendCode('', (string) st); + $this->compiler->appendPrefixCode($this->compiler->appendCode($tmp, "")); + res = $prefixVar; +} + +value(res) ::= value(v) modifierlist(l). { + res = $this->compiler->compileModifier(l, v); +} + // name space constant +value(res) ::= NAMESPACE(c). { + res = c; +} + + // array +value(res) ::= arraydef(a). { + res = a; +} + // static class access +value(res) ::= ns1(c)DOUBLECOLON static_class_access(s). { + if (!in_array(strtolower(c), array('self', 'parent')) && (!$this->security || $this->security->isTrustedStaticClassAccess(c, s, $this->compiler))) { + if (isset($this->smarty->registered_classes[c])) { + res = $this->smarty->registered_classes[c].'::'.s[0].s[1]; + } else { + res = c.'::'.s[0].s[1]; + } + } else { + $this->compiler->trigger_template_error ('static class \''.c.'\' is undefined or not allowed by security setting'); + } +} +// +// namespace stuff +// + +ns1(res) ::= ID(i). { + res = i; +} + +ns1(res) ::= NAMESPACE(i). { + res = i; + } + + + + +// +// variables +// + // Smarty variable (optional array) +variable(res) ::= DOLLARID(i). { + res = $this->compiler->compileVariable('\''.substr(i,1).'\''); +} +variable(res) ::= varindexed(vi). { + if (vi['var'] === '\'smarty\'') { + $smarty_var = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']); + res = $smarty_var; + } else { + // used for array reset,next,prev,end,current + $this->last_variable = vi['var']; + $this->last_index = vi['smarty_internal_index']; + res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index']; + } +} + + // variable with property +variable(res) ::= varvar(v) AT ID(p). { + res = '$_smarty_tpl->getVariable('. v .')->'.p; +} + + // object +variable(res) ::= object(o). { + res = o; +} + + // config variable +variable(res) ::= HATCH ID(i) HATCH. { + res = $this->compiler->compileConfigVariable('\'' . i . '\''); +} + +variable(res) ::= HATCH ID(i) HATCH arrayindex(a). { + res = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . i . '\'') . ') ? $tmp'.a.' :null)'; +} + +variable(res) ::= HATCH variable(v) HATCH. { + res = $this->compiler->compileConfigVariable(v); +} + +variable(res) ::= HATCH variable(v) HATCH arrayindex(a). { + res = '(is_array($tmp = ' . $this->compiler->compileConfigVariable(v) . ') ? $tmp'.a.' : null)'; +} + +varindexed(res) ::= DOLLARID(i) arrayindex(a). { + res = array('var'=>'\''.substr(i,1).'\'', 'smarty_internal_index'=>a); +} +varindexed(res) ::= varvar(v) arrayindex(a). { + res = array('var'=>v, 'smarty_internal_index'=>a); +} + +// +// array index +// + // multiple array index +arrayindex(res) ::= arrayindex(a1) indexdef(a2). { + res = a1.a2; +} + + // no array index +arrayindex ::= . { + return; +} + +// single index definition + // Smarty2 style index +indexdef(res) ::= DOT DOLLARID(i). { + res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']'; +} +indexdef(res) ::= DOT varvar(v). { + res = '['.$this->compiler->compileVariable(v).']'; +} + +indexdef(res) ::= DOT varvar(v) AT ID(p). { + res = '['.$this->compiler->compileVariable(v).'->'.p.']'; +} + +indexdef(res) ::= DOT ID(i). { + res = '[\''. i .'\']'; +} + +indexdef(res) ::= DOT INTEGER(n). { + res = '['. n .']'; +} + + +indexdef(res) ::= DOT LDEL expr(e) RDEL. { + res = '['. e .']'; +} + + // section tag index +indexdef(res) ::= OPENB ID(i)CLOSEB. { + res = '['.(new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,'[\'section\'][\''.i.'\'][\'index\']').']'; +} + +indexdef(res) ::= OPENB ID(i) DOT ID(i2) CLOSEB. { + res = '['.(new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,'[\'section\'][\''.i.'\'][\''.i2.'\']').']'; +} +indexdef(res) ::= OPENB SINGLEQUOTESTRING(s) CLOSEB. { + res = '['.s.']'; +} +indexdef(res) ::= OPENB INTEGER(n) CLOSEB. { + res = '['.n.']'; +} +indexdef(res) ::= OPENB DOLLARID(i) CLOSEB. { + res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']'; +} +indexdef(res) ::= OPENB variable(v) CLOSEB. { + res = '['.v.']'; +} +indexdef(res) ::= OPENB value(v) CLOSEB. { + res = '['.v.']'; +} + + // PHP style index +indexdef(res) ::= OPENB expr(e) CLOSEB. { + res = '['. e .']'; +} + + // for assign append array +indexdef(res) ::= OPENB CLOSEB. { + res = '[]'; +} + + +// +// variable variable names +// + + // singel identifier element +varvar(res) ::= DOLLARID(i). { + res = '\''.substr(i,1).'\''; +} + // single $ +varvar(res) ::= DOLLAR. { + res = '\'\''; +} + + // sequence of identifier elements +varvar(res) ::= varvar(v1) varvarele(v2). { + res = v1.'.'.v2; +} + + // fix sections of element +varvarele(res) ::= ID(s). { + res = '\''.s.'\''; +} +varvarele(res) ::= SIMPELOUTPUT(i). { + $var = trim(substr(i, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); + res = $this->compiler->compileVariable('\''.$var.'\''); +} + + // variable sections of element +varvarele(res) ::= LDEL expr(e) RDEL. { + res = '('.e.')'; +} + +// +// objects +// +object(res) ::= varindexed(vi) objectchain(oc). { + if (vi['var'] === '\'smarty\'') { + res = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']).oc; + } else { + res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index'].oc; + } +} + + // single element +objectchain(res) ::= objectelement(oe). { + res = oe; +} + + // chain of elements +objectchain(res) ::= objectchain(oc) objectelement(oe). { + res = oc.oe; +} + + // variable +objectelement(res)::= PTR ID(i) arrayindex(a). { + if ($this->security && substr(i,0,1) === '_') { + $this->compiler->trigger_template_error (self::ERR1); + } + res = '->'.i.a; +} + +objectelement(res)::= PTR varvar(v) arrayindex(a). { + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + res = '->{'.$this->compiler->compileVariable(v).a.'}'; +} + +objectelement(res)::= PTR LDEL expr(e) RDEL arrayindex(a). { + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + res = '->{'.e.a.'}'; +} + +objectelement(res)::= PTR ID(ii) LDEL expr(e) RDEL arrayindex(a). { + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + res = '->{\''.ii.'\'.'.e.a.'}'; +} + + // method +objectelement(res)::= PTR method(f). { + res = '->'.f; +} + + +// +// function +// +function(res) ::= ns1(f) OPENP params(p) CLOSEP. { + res = $this->compiler->compileModifierInExpression(f, p); +} + + +// +// method +// +method(res) ::= ID(f) OPENP params(p) CLOSEP. { + if ($this->security && substr(f,0,1) === '_') { + $this->compiler->trigger_template_error (self::ERR1); + } + res = f . '('. implode(',',p) .')'; +} + +method(res) ::= DOLLARID(f) OPENP params(p) CLOSEP. { + if ($this->security) { + $this->compiler->trigger_template_error (self::ERR2); + } + $prefixVar = $this->compiler->getNewPrefixVariable(); + $this->compiler->appendPrefixCode("compiler->compileVariable('\''.substr(f,1).'\'').';?>'); + res = $prefixVar .'('. implode(',',p) .')'; +} + +// function/method parameter + // multiple parameters +params(res) ::= params(p) COMMA expr(e). { + res = array_merge(p,array(e)); +} + + // single parameter +params(res) ::= expr(e). { + res = array(e); +} + + // no parameter +params(res) ::= . { + res = array(); +} + +// +// modifier +// +modifierlist(res) ::= modifierlist(l) modifier(m) modparameters(p). { + res = array_merge(l,array(array_merge(m,p))); +} + +modifierlist(res) ::= modifier(m) modparameters(p). { + res = array(array_merge(m,p)); +} + +modifier(res) ::= VERT AT ID(m). { + res = array(m); +} + +modifier(res) ::= VERT ID(m). { + res = array(m); +} + +// +// modifier parameter +// + // multiple parameter +modparameters(res) ::= modparameters(mps) modparameter(mp). { + res = array_merge(mps,mp); +} + + // no parameter +modparameters(res) ::= . { + res = array(); +} + + // parameter expression +modparameter(res) ::= COLON value(mp). { + res = array(mp); +} +modparameter(res) ::= COLON UNIMATH(m) value(mp). { + res = array(trim(m).mp); +} + +modparameter(res) ::= COLON array(mp). { + res = array(mp); +} + + // static class methode call +static_class_access(res) ::= method(m). { + res = array(m, '', 'method'); +} + + // static class methode call with object chainig +static_class_access(res) ::= method(m) objectchain(oc). { + res = array(m, oc, 'method'); +} + + // static class constant +static_class_access(res) ::= ID(v). { + res = array(v, ''); +} + + // static class variables +static_class_access(res) ::= DOLLARID(v) arrayindex(a). { + res = array(v, a, 'property'); +} + + // static class variables with object chain +static_class_access(res) ::= DOLLARID(v) arrayindex(a) objectchain(oc). { + res = array(v, a.oc, 'property'); +} + + +// if conditions and operators +lop(res) ::= LOGOP(o). { + res = ' '. trim(o) . ' '; +} + +lop(res) ::= SLOGOP(o). { + static $lops = array( + 'eq' => ' == ', + 'ne' => ' != ', + 'neq' => ' != ', + 'gt' => ' > ', + 'ge' => ' >= ', + 'gte' => ' >= ', + 'lt' => ' < ', + 'le' => ' <= ', + 'lte' => ' <= ', + 'mod' => ' % ', + 'and' => ' && ', + 'or' => ' || ', + 'xor' => ' xor ', + ); + $op = strtolower(preg_replace('/\s*/', '', o)); + res = $lops[$op]; +} +tlop(res) ::= TLOGOP(o). { + static $tlops = array( + 'isdivby' => array('op' => ' % ', 'pre' => '!('), + 'isnotdivby' => array('op' => ' % ', 'pre' => '('), + 'isevenby' => array('op' => ' / ', 'pre' => '!(1 & '), + 'isnotevenby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isoddby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isnotoddby' => array('op' => ' / ', 'pre' => '!(1 & '), + ); + $op = strtolower(preg_replace('/\s*/', '', o)); + res = $tlops[$op]; + } + +scond(res) ::= SINGLECOND(o). { + static $scond = array ( + 'iseven' => '!(1 & ', + 'isnoteven' => '(1 & ', + 'isodd' => '(1 & ', + 'isnotodd' => '!(1 & ', + ); + $op = strtolower(str_replace(' ', '', o)); + res = $scond[$op]; +} + +// +// ARRAY element assignment +// +arraydef(res) ::= OPENB arrayelements(a) CLOSEB. { + res = 'array('.a.')'; +} +arraydef(res) ::= ARRAYOPEN arrayelements(a) CLOSEP. { + res = 'array('.a.')'; +} + +arrayelements(res) ::= arrayelement(a). { + res = a; +} + +arrayelements(res) ::= arrayelements(a1) COMMA arrayelement(a). { + res = a1.','.a; +} + +arrayelements ::= . { + return; +} + +arrayelement(res) ::= value(e1) APTR expr(e2). { + res = e1.'=>'.e2; +} + +arrayelement(res) ::= ID(i) APTR expr(e2). { + res = '\''.i.'\'=>'.e2; +} + +arrayelement(res) ::= expr(e). { + res = e; +} + + +// +// double quoted strings +// +doublequoted_with_quotes(res) ::= QUOTE QUOTE. { + res = '\'\''; +} + +doublequoted_with_quotes(res) ::= QUOTE doublequoted(s) QUOTE. { + $this->compiler->leaveDoubleQuote(); + res = s->to_smarty_php($this); +} + + +doublequoted(res) ::= doublequoted(o1) doublequotedcontent(o2). { + o1->append_subtree($this, o2); + res = o1; +} + +doublequoted(res) ::= doublequotedcontent(o). { + res = new Dq($this, o); +} + +doublequotedcontent(res) ::= BACKTICK variable(v) BACKTICK. { + res = new Code('(string)'.v); +} + +doublequotedcontent(res) ::= BACKTICK expr(e) BACKTICK. { + res = new Code('(string)('.e.')'); +} + +doublequotedcontent(res) ::= DOLLARID(i). { + res = new Code('(string)$_smarty_tpl->getValue(\''. substr(i,1) .'\')'); +} + +doublequotedcontent(res) ::= LDEL variable(v) RDEL. { + res = new Code('(string)'.v); +} + +doublequotedcontent(res) ::= LDEL expr(e) RDEL. { + res = new Code('(string)('.e.')'); +} + +doublequotedcontent(res) ::= smartytag(st). { + res = new Tag($this, st); +} + +doublequotedcontent(res) ::= TEXT(o). { + res = new DqContent(o); +} + diff --git a/src/Resource/BasePlugin.php b/src/Resource/BasePlugin.php new file mode 100644 index 0000000..56f7dfa --- /dev/null +++ b/src/Resource/BasePlugin.php @@ -0,0 +1,145 @@ + FilePlugin::class, + 'string' => StringPlugin::class, + 'extends' => ExtendsPlugin::class, + 'stream' => StreamPlugin::class, + 'eval' => StringEval::class, + ]; + + /** + * Source must be recompiled on every occasion + * + * @var boolean + */ + public $recompiled = false; + + /** + * Flag if resource does allow compilation + * + * @return bool + */ + public function supportsCompiledTemplates(): bool { + return true; + } + + /** + * Check if resource must check time stamps when loading compiled or cached templates. + * Resources like 'extends' which use source components my disable timestamp checks on own resource. + * @return bool + */ + public function checkTimestamps() + { + return true; + } + + /** + * Load Resource Handler + * + * @param Smarty $smarty smarty object + * @param string $type name of the resource + * + * @return BasePlugin Resource Handler + * @throws Exception + */ + public static function load(Smarty $smarty, $type) + { + // try smarty's cache + if (isset($smarty->_resource_handlers[ $type ])) { + return $smarty->_resource_handlers[ $type ]; + } + // try registered resource + if (isset($smarty->registered_resources[ $type ])) { + return $smarty->_resource_handlers[ $type ] = $smarty->registered_resources[ $type ]; + } + // try sysplugins dir + if (isset(self::$sysplugins[ $type ])) { + $_resource_class = self::$sysplugins[ $type ]; + return $smarty->_resource_handlers[ $type ] = new $_resource_class(); + } + // try plugins dir + $_resource_class = 'Smarty_Resource_' . \smarty_ucfirst_ascii($type); + if (class_exists($_resource_class, false)) { + return $smarty->_resource_handlers[ $type ] = new $_resource_class(); + } + // try streams + $_known_stream = stream_get_wrappers(); + if (in_array($type, $_known_stream)) { + // is known stream + if (is_object($smarty->security_policy)) { + $smarty->security_policy->isTrustedStream($type); + } + return $smarty->_resource_handlers[ $type ] = new StreamPlugin(); + } + // TODO: try default_(template|config)_handler + // give up + throw new \Smarty\Exception("Unknown resource type '{$type}'"); + } + + /** + * Load template's source into current template object + * + * @param Source $source source object + * + * @return string template source + * @throws \Smarty\Exception if source cannot be loaded + */ + abstract public function getContent(Source $source); + + /** + * populate Source Object with metadata from Resource + * + * @param Source $source source object + * @param Template|null $_template template object + */ + abstract public function populate(Source $source, \Smarty\Template $_template = null); + + /** + * populate Source Object with timestamp and exists from Resource + * + * @param Source $source source object + */ + public function populateTimestamp(Source $source) + { + // intentionally left blank + } + + /* + * Check if resource must check time stamps when when loading complied or cached templates. + * Resources like 'extends' which use source components my disable timestamp checks on own resource. + * + * @return bool + */ + /** + * Determine basename for compiled filename + * + * @param \Smarty\Template\Source $source source object + * + * @return string resource's basename + */ + public function getBasename(\Smarty\Template\Source $source) + { + return basename(preg_replace('![^\w]+!', '_', $source->name)); + } + +} diff --git a/src/Resource/CustomPlugin.php b/src/Resource/CustomPlugin.php new file mode 100644 index 0000000..b50ef7a --- /dev/null +++ b/src/Resource/CustomPlugin.php @@ -0,0 +1,105 @@ +uid = sha1($source->type . ':' . $source->name); + $mtime = $this->fetchTimestamp($source->name); + if ($mtime !== null) { + $source->timestamp = $mtime; + } else { + $this->fetch($source->name, $content, $timestamp); + $source->timestamp = $timestamp ?? false; + if (isset($content)) { + $source->content = $content; + } + } + $source->exists = !!$source->timestamp; + } + + /** + * Load template's source into current template object + * + * @param Source $source source object + * + * @return string template source + * @throws Exception if source cannot be loaded + */ + public function getContent(Source $source) { + $this->fetch($source->name, $content, $timestamp); + if (isset($content)) { + return $content; + } + throw new Exception("Unable to read template {$source->type} '{$source->name}'"); + } + + /** + * Determine basename for compiled filename + * + * @param Source $source source object + * + * @return string resource's basename + */ + public function getBasename(Source $source) { + return basename($this->generateSafeName($source->name)); + } + + /** + * Removes special characters from $name and limits its length to 127 characters. + * + * @param $name + * + * @return string + */ + private function generateSafeName($name): string { + return substr(preg_replace('/[^A-Za-z0-9._]/', '', (string)$name), 0, 127); + } +} diff --git a/src/sysplugins/smarty_internal_resource_extends.php b/src/Resource/ExtendsPlugin.php similarity index 50% rename from src/sysplugins/smarty_internal_resource_extends.php rename to src/Resource/ExtendsPlugin.php index 8094693..acce54e 100644 --- a/src/sysplugins/smarty_internal_resource_extends.php +++ b/src/Resource/ExtendsPlugin.php @@ -1,59 +1,45 @@ name); - $smarty = &$source->smarty; + $smarty = $source->getSmarty(); $exists = true; foreach ($components as $component) { - /* @var \Smarty_Template_Source $_s */ - $_s = Smarty_Template_Source::load(null, $smarty, $component); - if ($_s->type === 'php') { - throw new SmartyException("Resource type {$_s->type} cannot be used with the extends resource type"); - } + $_s = Source::load(null, $smarty, $component); $sources[ $_s->uid ] = $_s; - $uid .= $_s->filepath; + $uid .= $_s->uid; if ($_template) { $exists = $exists && $_s->exists; } } $source->components = $sources; - $source->filepath = $_s->filepath; - $source->uid = sha1($uid . $source->smarty->_joined_template_dir); + $source->uid = sha1($uid . $source->getSmarty()->_joined_template_dir); $source->exists = $exists; if ($_template) { $source->timestamp = $_s->timestamp; @@ -63,12 +49,12 @@ class Smarty_Internal_Resource_Extends extends Smarty_Resource /** * populate Source Object with timestamp and exists from Resource * - * @param Smarty_Template_Source $source source object + * @param Source $source source object */ - public function populateTimestamp(Smarty_Template_Source $source) + public function populateTimestamp(Source $source) { $source->exists = true; - /* @var \Smarty_Template_Source $_s */ + /* @var Source $_s */ foreach ($source->components as $_s) { $source->exists = $source->exists && $_s->exists; } @@ -78,19 +64,19 @@ class Smarty_Internal_Resource_Extends extends Smarty_Resource /** * Load template's source from files into current template object * - * @param Smarty_Template_Source $source source object + * @param Source $source source object * * @return string template source - * @throws SmartyException if source cannot be loaded + * @throws \Smarty\Exception if source cannot be loaded */ - public function getContent(Smarty_Template_Source $source) + public function getContent(Source $source) { if (!$source->exists) { - throw new SmartyException("Unable to load template '{$source->type}:{$source->name}'"); + throw new \Smarty\Exception("Unable to load '{$source->type}:{$source->name}'"); } $_components = array_reverse($source->components); $_content = ''; - /* @var \Smarty_Template_Source $_s */ + /* @var Source $_s */ foreach ($_components as $_s) { // read content $_content .= $_s->getContent(); @@ -101,13 +87,13 @@ class Smarty_Internal_Resource_Extends extends Smarty_Resource /** * Determine basename for compiled filename * - * @param Smarty_Template_Source $source source object + * @param Source $source source object * * @return string resource's basename */ - public function getBasename(Smarty_Template_Source $source) + public function getBasename(Source $source) { - return str_replace(':', '.', basename($source->filepath)); + return str_replace(':', '.', basename($source->getResourceName())); } /* diff --git a/src/Resource/FilePlugin.php b/src/Resource/FilePlugin.php new file mode 100644 index 0000000..7fd8667 --- /dev/null +++ b/src/Resource/FilePlugin.php @@ -0,0 +1,180 @@ +uid = sha1( + $source->name . ($source->isConfig ? $source->getSmarty()->_joined_config_dir : + $source->getSmarty()->_joined_template_dir) + ); + + if ($path = $this->getFilePath($source->name, $source->getSmarty(), $source->isConfig)) { + if (isset($source->getSmarty()->security_policy) && is_object($source->getSmarty()->security_policy)) { + $source->getSmarty()->security_policy->isTrustedResourceDir($path, $source->isConfig); + } + $source->exists = true; + $source->timestamp = filemtime($path); + } else { + $source->timestamp = $source->exists = false; + } + } + + /** + * populate Source Object with timestamp and exists from Resource + * + * @param Source $source source object + */ + public function populateTimestamp(Source $source) { + if (!$source->exists && $path = $this->getFilePath($source->name, $source->getSmarty(), $source->isConfig)) { + $source->timestamp = $source->exists = is_file($path); + } + if ($source->exists && $path) { + $source->timestamp = filemtime($path); + } + } + + /** + * Load template's source from file into current template object + * + * @param Source $source source object + * + * @return string template source + * @throws Exception if source cannot be loaded + */ + public function getContent(Source $source) { + if ($source->exists) { + return file_get_contents($this->getFilePath($source->getResourceName(), $source->getSmarty(), $source->isConfig())); + } + throw new Exception( + 'Unable to read ' . ($source->isConfig ? 'config' : 'template') . + " {$source->type} '{$source->name}'" + ); + } + + /** + * Determine basename for compiled filename + * + * @param Source $source source object + * + * @return string resource's basename + */ + public function getBasename(Source $source) { + return basename($source->getResourceName()); + } + + /** + * build template filepath by traversing the template_dir array + * + * @param $file + * @param Smarty $smarty + * @param bool $isConfig + * + * @return string fully qualified filepath + */ + public function getFilePath($file, \Smarty\Smarty $smarty, bool $isConfig = false) { + // absolute file ? + if ($file[0] === '/' || $file[1] === ':') { + $file = $smarty->_realpath($file, true); + return is_file($file) ? $file : false; + } + + // normalize DIRECTORY_SEPARATOR + if (strpos($file, DIRECTORY_SEPARATOR === '/' ? '\\' : '/') !== false) { + $file = str_replace(DIRECTORY_SEPARATOR === '/' ? '\\' : '/', DIRECTORY_SEPARATOR, $file); + } + $_directories = $smarty->getTemplateDir(null, $isConfig); + // template_dir index? + if ($file[0] === '[' && preg_match('#^\[([^\]]+)\](.+)$#', $file, $fileMatch)) { + $file = $fileMatch[2]; + $_indices = explode(',', $fileMatch[1]); + $_index_dirs = []; + foreach ($_indices as $index) { + $index = trim($index); + // try string indexes + if (isset($_directories[$index])) { + $_index_dirs[] = $_directories[$index]; + } elseif (is_numeric($index)) { + // try numeric index + $index = (int)$index; + if (isset($_directories[$index])) { + $_index_dirs[] = $_directories[$index]; + } else { + // try at location index + $keys = array_keys($_directories); + if (isset($_directories[$keys[$index]])) { + $_index_dirs[] = $_directories[$keys[$index]]; + } + } + } + } + if (empty($_index_dirs)) { + // index not found + return false; + } else { + $_directories = $_index_dirs; + } + } + // relative file name? + foreach ($_directories as $_directory) { + $path = $_directory . $file; + if (is_file($path)) { + return (strpos($path, '.' . DIRECTORY_SEPARATOR) !== false) ? $smarty->_realpath($path) : $path; + } + } + if (!isset($_index_dirs)) { + // Could be relative to cwd + $path = $smarty->_realpath($file, true); + if (is_file($path)) { + return $path; + } + } + return false; + } + + /** + * Returns the timestamp of the resource indicated by $resourceName, or false if it doesn't exist. + * + * @param string $resourceName + * @param Smarty $smarty + * @param bool $isConfig + * + * @return false|int + */ + public function getResourceNameTimestamp(string $resourceName, \Smarty\Smarty $smarty, bool $isConfig = false) { + if ($path = $this->getFilePath($resourceName, $smarty, $isConfig)) { + return filemtime($path); + } + return false; + } +} diff --git a/src/Resource/RecompiledPlugin.php b/src/Resource/RecompiledPlugin.php new file mode 100644 index 0000000..f1c64bc --- /dev/null +++ b/src/Resource/RecompiledPlugin.php @@ -0,0 +1,50 @@ +uid = false; + $source->content = $this->getContent($source); + $source->timestamp = $source->exists = !!$source->content; + } + + /** + * Load template's source from stream into current template object + * + * @param Source $source source object + * + * @return string template source + */ + public function getContent(Source $source) { + + if (strpos($source->getResourceName(), '://') !== false) { + $filepath = $source->getResourceName(); + } else { + $filepath = str_replace(':', '://', $source->getFullResourceName()); + } + + $t = ''; + // the availability of the stream has already been checked in Smarty\Resource\Base::fetch() + $fp = fopen($filepath, 'r+'); + if ($fp) { + while (!feof($fp) && ($current_line = fgets($fp)) !== false) { + $t .= $current_line; + } + fclose($fp); + return $t; + } else { + return false; + } + } + +} diff --git a/src/sysplugins/smarty_internal_resource_eval.php b/src/Resource/StringEval.php similarity index 53% rename from src/sysplugins/smarty_internal_resource_eval.php rename to src/Resource/StringEval.php index 3b552a5..5c35e74 100644 --- a/src/sysplugins/smarty_internal_resource_eval.php +++ b/src/Resource/StringEval.php @@ -1,9 +1,14 @@ uid = $source->filepath = sha1($source->name); + $source->uid = sha1($source->name); $source->timestamp = $source->exists = true; } /** * Load template's source from $resource_name into current template object * - * @uses decode() to decode base64 and urlencoded template_resources - * - * @param Smarty_Template_Source $source source object + * @param \Smarty\Template\Source $source source object * * @return string template source + *@uses decode() to decode base64 and urlencoded template_resources + * */ - public function getContent(Smarty_Template_Source $source) + public function getContent(\Smarty\Template\Source $source) { return $this->decode($source->name); } @@ -66,28 +71,14 @@ class Smarty_Internal_Resource_Eval extends Smarty_Resource_Recompiled return $string; } - /** - * modify resource_name according to resource handlers specifications - * - * @param Smarty $smarty Smarty instance - * @param string $resource_name resource_name to make unique - * @param boolean $isConfig flag for config resource - * - * @return string unique resource name - */ - public function buildUniqueResourceName(Smarty $smarty, $resource_name, $isConfig = false) - { - return get_class($this) . '#' . $this->decode($resource_name); - } - /** * Determine basename for compiled filename * - * @param Smarty_Template_Source $source source object + * @param \Smarty\Template\Source $source source object * * @return string resource's basename */ - public function getBasename(Smarty_Template_Source $source) + public function getBasename(\Smarty\Template\Source $source) { return ''; } diff --git a/src/Resource/StringPlugin.php b/src/Resource/StringPlugin.php new file mode 100644 index 0000000..6b5bee4 --- /dev/null +++ b/src/Resource/StringPlugin.php @@ -0,0 +1,94 @@ +uid = sha1($source->name); + $source->timestamp = $source->exists = true; + } + + /** + * Load template's source from $resource_name into current template object + * + * @param Source $source source object + * + * @return string template source + * @uses decode() to decode base64 and urlencoded template_resources + * + */ + public function getContent(Source $source) { + return $this->decode($source->name); + } + + /** + * decode base64 and urlencode + * + * @param string $string template_resource to decode + * + * @return string decoded template_resource + */ + protected function decode($string) { + // decode if specified + if (($pos = strpos($string, ':')) !== false) { + if (!strncmp($string, 'base64', 6)) { + return base64_decode(substr($string, 7)); + } elseif (!strncmp($string, 'urlencode', 9)) { + return urldecode(substr($string, 10)); + } + } + return $string; + } + + /** + * Determine basename for compiled filename + * Always returns an empty string. + * + * @param Source $source source object + * + * @return string resource's basename + */ + public function getBasename(Source $source) { + return ''; + } + + /* + * Disable timestamp checks for string resource. + * + * @return bool + */ + /** + * @return bool + */ + public function checkTimestamps() { + return false; + } +} diff --git a/src/sysplugins/smarty_internal_block.php b/src/Runtime/Block.php similarity index 81% rename from src/sysplugins/smarty_internal_block.php rename to src/Runtime/Block.php index 9956d64..90eab9c 100644 --- a/src/sysplugins/smarty_internal_block.php +++ b/src/Runtime/Block.php @@ -1,13 +1,15 @@ registerCallbacks($_template); + + $this->captureStack[] = [ + $buffer, + $assign, + $append, + ]; + $this->captureCount++; + ob_start(); + } + + /** + * Register callbacks in template class + * + * @param \Smarty\Template $_template + */ + private function registerCallbacks(Template $_template) { + + foreach ($_template->startRenderCallbacks as $callback) { + if (is_array($callback) && get_class($callback[0]) == self::class) { + // already registered + return; + } + } + + $_template->startRenderCallbacks[] = [ + $this, + 'startRender', + ]; + $_template->endRenderCallbacks[] = [ + $this, + 'endRender', + ]; + $this->startRender($_template); + } + + /** + * Start render callback + * + * @param \Smarty\Template $_template + */ + public function startRender(Template $_template) { + $this->countStack[] = $this->captureCount; + $this->captureCount = 0; + } + + /** + * Close capture section + * + * @param \Smarty\Template $_template + * + * @throws \Smarty\Exception + */ + public function close(Template $_template) { + if ($this->captureCount) { + [$buffer, $assign, $append] = array_pop($this->captureStack); + $this->captureCount--; + if (isset($assign)) { + $_template->assign($assign, ob_get_contents()); + } + if (isset($append)) { + $_template->append($append, ob_get_contents()); + } + $this->namedBuffer[$buffer] = ob_get_clean(); + } else { + $this->error($_template); + } + } + + /** + * Error exception on not matching {capture}{/capture} + * + * @param \Smarty\Template $_template + * + * @throws \Smarty\Exception + */ + public function error(Template $_template) { + throw new \Smarty\Exception("Not matching {capture}{/capture} in '{$_template->template_resource}'"); + } + + /** + * Return content of named capture buffer by key or as array + * + * @param \Smarty\Template $_template + * @param string|null $name + * + * @return string|string[]|null + */ + public function getBuffer(Template $_template, $name = null) { + if (isset($name)) { + return $this->namedBuffer[$name] ?? null; + } else { + return $this->namedBuffer; + } + } + + /** + * End render callback + * + * @param \Smarty\Template $_template + * + * @throws \Smarty\Exception + */ + public function endRender(Template $_template) { + if ($this->captureCount) { + $this->error($_template); + } else { + $this->captureCount = array_pop($this->countStack); + } + } +} diff --git a/src/Runtime/DefaultPluginHandlerRuntime.php b/src/Runtime/DefaultPluginHandlerRuntime.php new file mode 100644 index 0000000..ad6ed74 --- /dev/null +++ b/src/Runtime/DefaultPluginHandlerRuntime.php @@ -0,0 +1,73 @@ +defaultPluginHandler = $defaultPluginHandler; + } + + public function hasPlugin($tag, $plugin_type): bool { + if ($this->defaultPluginHandler === null) { + return false; + } + + $callback = null; + + // these are not used here + $script = null; + $cacheable = null; + + return (call_user_func_array( + $this->defaultPluginHandler, + [ + $tag, + $plugin_type, + null, // This used to pass $this->template, but this parameter has been removed in 5.0 + &$callback, + &$script, + &$cacheable, + ] + ) && $callback); + } + + /** + * @throws Exception + */ + public function getCallback($tag, $plugin_type) { + + if ($this->defaultPluginHandler === null) { + return false; + } + + $callback = null; + + // these are not used here + $script = null; + $cacheable = null; + + if (call_user_func_array( + $this->defaultPluginHandler, + [ + $tag, + $plugin_type, + null, // This used to pass $this->template, but this parameter has been removed in 5.0 + &$callback, + &$script, + &$cacheable, + ] + ) && $callback) { + return $callback; + } + throw new Exception("Default plugin handler: Returned callback for '{$tag}' not callable at runtime"); + } + +} \ No newline at end of file diff --git a/src/Runtime/ForeachRuntime.php b/src/Runtime/ForeachRuntime.php new file mode 100644 index 0000000..06da7d5 --- /dev/null +++ b/src/Runtime/ForeachRuntime.php @@ -0,0 +1,160 @@ +count($from); + } + } else { + settype($from, 'array'); + } + } + if (!isset($total)) { + $total = empty($from) ? 0 : ($needTotal ? count($from) : 1); + } + if ($tpl->hasVariable($item)) { + $saveVars['item'] = [ + $item, + $tpl->getVariable($item)->getValue(), + ]; + } + $tpl->assign($item,null); + if ($total === 0) { + $from = null; + } else { + if ($key) { + if ($tpl->hasVariable($key)) { + $saveVars['key'] = [ + $key, + clone $tpl->getVariable($key), + ]; + } + $tpl->assign($key, null); + } + } + if ($needTotal) { + $tpl->getVariable($item)->total = $total; + } + if ($name) { + $namedVar = "__smarty_foreach_{$name}"; + if ($tpl->hasVariable($namedVar)) { + $saveVars['named'] = [ + $namedVar, + clone $tpl->getVariable($namedVar), + ]; + } + $namedProp = []; + if (isset($properties['total'])) { + $namedProp['total'] = $total; + } + if (isset($properties['iteration'])) { + $namedProp['iteration'] = 0; + } + if (isset($properties['index'])) { + $namedProp['index'] = -1; + } + if (isset($properties['show'])) { + $namedProp['show'] = ($total > 0); + } + $tpl->assign($namedVar, $namedProp); + } + $this->stack[] = $saveVars; + return $from; + } + + /** + * [util function] counts an array, arrayAccess/traversable or PDOStatement object + * + * @param mixed $value + * + * @return int the count for arrays and objects that implement countable, 1 for other objects that don't, and 0 + * for empty elements + * @throws \Exception + */ + public function count($value): int + { + if ($value instanceof \IteratorAggregate) { + // Note: getIterator() returns a Traversable, not an Iterator + // thus rewind() and valid() methods may not be present + return iterator_count($value->getIterator()); + } elseif ($value instanceof \Iterator) { + return $value instanceof \Generator ? 1 : iterator_count($value); + } elseif ($value instanceof \Countable) { + return count($value); + } + return count((array) $value); + } + + /** + * Restore saved variables + * + * will be called by {break n} or {continue n} for the required number of levels + * + * @param \Smarty\Template $tpl + * @param int $levels number of levels + */ + public function restore(Template $tpl, $levels = 1) { + while ($levels) { + $saveVars = array_pop($this->stack); + if (!empty($saveVars)) { + if (isset($saveVars['item'])) { + $tpl->getVariable($saveVars['item'][0])->setValue($saveVars['item'][1]); + } + if (isset($saveVars['key'])) { + $tpl->setVariable($saveVars['key'][0], $saveVars['key'][1]); + } + if (isset($saveVars['named'])) { + $tpl->setVariable($saveVars['named'][0], $saveVars['named'][1]); + } + } + $levels--; + } + } +} diff --git a/src/Runtime/InheritanceRuntime.php b/src/Runtime/InheritanceRuntime.php new file mode 100644 index 0000000..ffd7aae --- /dev/null +++ b/src/Runtime/InheritanceRuntime.php @@ -0,0 +1,243 @@ +state === 3 && (strpos($tpl->template_resource, 'extendsall') === false)) { + $tpl->setInheritance(clone $tpl->getSmarty()->getRuntime('Inheritance')); + $tpl->getInheritance()->init($tpl, $initChild, $blockNames); + return; + } + ++$this->tplIndex; + $this->sources[$this->tplIndex] = $tpl->getSource(); + // start of child sub template(s) + if ($initChild) { + $this->state = 1; + if (!$this->inheritanceLevel) { + //grab any output of child templates + ob_start(); + } + ++$this->inheritanceLevel; + } + // if state was waiting for parent change state to parent + if ($this->state === 2) { + $this->state = 3; + } + } + + /** + * End of child template(s) + * - if outer level is reached flush output buffer and switch to wait for parent template state + * + * @param \Smarty\Template $tpl + * @param null|string $template optional name of inheritance parent template + * + * @throws \Exception + * @throws \Smarty\Exception + */ + public function endChild(Template $tpl, $template = null, ?string $currentDir = null) { + --$this->inheritanceLevel; + if (!$this->inheritanceLevel) { + ob_end_clean(); + $this->state = 2; + } + if (isset($template)) { + $tpl->renderSubTemplate( + $template, + $tpl->cache_id, + $tpl->compile_id, + $tpl->caching ? \Smarty\Template::CACHING_NOCACHE_CODE : 0, + $tpl->cache_lifetime, + [], + null, + $currentDir + ); + } + } + + /** + * \Smarty\Runtime\Block constructor. + * - if outer level {block} of child template ($state === 1) save it as child root block + * - otherwise process inheritance and render + * + * @param \Smarty\Template $tpl + * @param $className + * @param string $name + * @param int|null $tplIndex index of outer level {block} if nested + * + * @throws \Smarty\Exception + */ + public function instanceBlock(Template $tpl, $className, $name, $tplIndex = null) { + $block = new $className($name, isset($tplIndex) ? $tplIndex : $this->tplIndex); + if (isset($this->childRoot[$name])) { + $block->child = $this->childRoot[$name]; + } + if ($this->state === 1) { + $this->childRoot[$name] = $block; + return; + } + // make sure we got child block of child template of current block + while ($block->child && $block->child->child && $block->tplIndex <= $block->child->tplIndex) { + $block->child = $block->child->child; + } + $this->processBlock($tpl, $block); + } + + /** + * Goto child block or render this + * + * @param Template $tpl + * @param \Smarty\Runtime\Block $block + * @param \Smarty\Runtime\Block|null $parent + * + * @throws Exception + */ + private function processBlock( + Template $tpl, + \Smarty\Runtime\Block $block, + \Smarty\Runtime\Block $parent = null + ) { + if ($block->hide && !isset($block->child)) { + return; + } + if (isset($block->child) && $block->child->hide && !isset($block->child->child)) { + $block->child = null; + } + $block->parent = $parent; + if ($block->append && !$block->prepend && isset($parent)) { + $this->callParent($tpl, $block, '\'{block append}\''); + } + if ($block->callsChild || !isset($block->child) || ($block->child->hide && !isset($block->child->child))) { + $this->callBlock($block, $tpl); + } else { + $this->processBlock($tpl, $block->child, $block); + } + if ($block->prepend && isset($parent)) { + $this->callParent($tpl, $block, '{block prepend}'); + if ($block->append) { + if ($block->callsChild || !isset($block->child) + || ($block->child->hide && !isset($block->child->child)) + ) { + $this->callBlock($block, $tpl); + } else { + $this->processBlock($tpl, $block->child, $block); + } + } + } + $block->parent = null; + } + + /** + * Render child on \$smarty.block.child + * + * @param Template $tpl + * @param \Smarty\Runtime\Block $block + * + * @return null|string block content + * @throws Exception + */ + public function callChild(Template $tpl, \Smarty\Runtime\Block $block) { + if (isset($block->child)) { + $this->processBlock($tpl, $block->child, $block); + } + } + + /** + * Render parent block on \$smarty.block.parent or {block append/prepend} + * + * @param Template $tpl + * @param \Smarty\Runtime\Block $block + * @param string $tag + * + * @return null|string block content + * @throws Exception + */ + public function callParent(Template $tpl, \Smarty\Runtime\Block $block) { + if (isset($block->parent)) { + $this->callBlock($block->parent, $tpl); + } else { + throw new Exception("inheritance: illegal '{\$smarty.block.parent}' used in child template '" . + "{$tpl->getInheritance()->sources[$block->tplIndex]->getResourceName()}' block '{$block->name}'"); + } + } + + /** + * render block + * + * @param \Smarty\Runtime\Block $block + * @param Template $tpl + */ + public function callBlock(\Smarty\Runtime\Block $block, Template $tpl) { + $this->sourceStack[] = $tpl->getSource(); + $tpl->setSource($this->sources[$block->tplIndex]); + $block->callBlock($tpl); + $tpl->setSource(array_pop($this->sourceStack)); + } +} diff --git a/src/Runtime/TplFunctionRuntime.php b/src/Runtime/TplFunctionRuntime.php new file mode 100644 index 0000000..6c4b93d --- /dev/null +++ b/src/Runtime/TplFunctionRuntime.php @@ -0,0 +1,144 @@ +tplFunctions[$name] ?? ($tpl->getSmarty()->tplFunctions[$name] ?? null); + if (!isset($funcParam)) { + throw new \Smarty\Exception("Unable to find template function '{$name}'"); + } + + if (!$tpl->caching || ($tpl->caching && $nocache)) { + $function = $funcParam['call_name']; + } else { + if (isset($funcParam['call_name_caching'])) { + $function = $funcParam['call_name_caching']; + } else { + $function = $funcParam['call_name']; + } + } + if (!function_exists($function) && !$this->addTplFuncToCache($tpl, $name, $function)) { + throw new \Smarty\Exception("Unable to find template function '{$name}'"); + } + + $tpl->pushStack(); + $function($tpl, $params); + $tpl->popStack(); + } + + /** + * Register template functions defined by template + * + * @param \Smarty|\Smarty\Template|\Smarty\TemplateBase $obj + * @param array $tplFunctions source information array of + * template functions defined + * in template + * @param bool $override if true replace existing + * functions with same name + */ + public function registerTplFunctions(TemplateBase $obj, $tplFunctions, $override = true) { + $obj->tplFunctions = + $override ? array_merge($obj->tplFunctions, $tplFunctions) : array_merge($tplFunctions, $obj->tplFunctions); + // make sure that the template functions are known in parent templates + if ($obj->_isSubTpl()) { + $this->registerTplFunctions($obj->parent, $tplFunctions, false); + } else { + $obj->getSmarty()->tplFunctions = $override ? array_merge($obj->getSmarty()->tplFunctions, $tplFunctions) : + array_merge($tplFunctions, $obj->getSmarty()->tplFunctions); + } + } + + /** + * Return source parameter array for single or all template functions + * + * @param \Smarty\Template $tpl template object + * @param null|string $name template function name + * + * @return array|bool|mixed + */ + public function getTplFunction(Template $tpl, $name = null) { + if (isset($name)) { + return $tpl->tplFunctions[$name] ?? ($tpl->getSmarty()->tplFunctions[$name] ?? false); + } else { + return empty($tpl->tplFunctions) ? $tpl->getSmarty()->tplFunctions : $tpl->tplFunctions; + } + } + + /** + * Add template function to cache file for nocache calls + * + * @param Template $tpl + * @param string $_name template function name + * @param string $_function PHP function name + * + * @return bool + * @throws Exception + */ + private function addTplFuncToCache(Template $tpl, $_name, $_function) { + $funcParam = $tpl->tplFunctions[$_name]; + if (is_file($funcParam['compiled_filepath'])) { + // read compiled file + $code = file_get_contents($funcParam['compiled_filepath']); + // grab template function + if (preg_match("/\/\* {$_function} \*\/([\S\s]*?)\/\*\/ {$_function} \*\//", $code, $match)) { + // grab source info from file dependency + preg_match("/\s*'{$funcParam['uid']}'([\S\s]*?)\),/", $code, $match1); + unset($code); + // make PHP function known + eval($match[0]); + if (function_exists($_function)) { + + // Some magic code existed here, testing if the cached property had been set + // and then bubbling up until it found a parent template that had the cached property. + // This is no longer possible, so somehow this might break. + + // add template function code to cache file + $content = $tpl->getCached()->readCache($tpl); + if ($content) { + // check if we must update file dependency + if (!preg_match("/'{$funcParam['uid']}'(.*?)'nocache_hash'/", $content, $match2)) { + $content = preg_replace("/('file_dependency'(.*?)\()/", "\\1{$match1[0]}", $content); + } + $tpl->getCached()->writeCache( + $tpl, + preg_replace('/\s*\?>\s*$/', "\n", $content) . + "\n" . preg_replace( + [ + '/^\s*<\?php\s+/', + '/\s*\?>\s*$/', + ], + "\n", + $match[0] + ) + ); + } + return true; + } + } + } + return false; + } + +} diff --git a/src/Security.php b/src/Security.php new file mode 100644 index 0000000..250b3bc --- /dev/null +++ b/src/Security.php @@ -0,0 +1,560 @@ + array('method_1', 'method_2'), // allowed methods listed + * 'class_2' => array(), // all methods of class allowed + * ) + * If set to null none is allowed. + * + * @var array + */ + public $trusted_static_methods = []; + + /** + * This is an array of trusted static properties. + * If empty access to all static classes and properties is allowed. + * Format: + * array ( + * 'class_1' => array('prop_1', 'prop_2'), // allowed properties listed + * 'class_2' => array(), // all properties of class allowed + * ) + * If set to null none is allowed. + * + * @var array + */ + public $trusted_static_properties = []; + + /** + * This is an array of allowed tags. + * If empty no restriction by allowed_tags. + * + * @var array + */ + public $allowed_tags = []; + + /** + * This is an array of disabled tags. + * If empty no restriction by disabled_tags. + * + * @var array + */ + public $disabled_tags = []; + + /** + * This is an array of allowed modifier plugins. + * If empty no restriction by allowed_modifiers. + * + * @var array + */ + public $allowed_modifiers = []; + + /** + * This is an array of disabled modifier plugins. + * If empty no restriction by disabled_modifiers. + * + * @var array + */ + public $disabled_modifiers = []; + + /** + * This is an array of disabled special $smarty variables. + * + * @var array + */ + public $disabled_special_smarty_vars = []; + + /** + * This is an array of trusted streams. + * If empty all streams are allowed. + * To disable all streams set $streams = null. + * + * @var array + */ + public $streams = ['file']; + + /** + * + flag if constants can be accessed from template + * + * @var boolean + */ + public $allow_constants = true; + + /** + * + flag if super globals can be accessed from template + * + * @var boolean + */ + public $allow_super_globals = true; + + /** + * max template nesting level + * + * @var int + */ + public $max_template_nesting = 0; + + /** + * current template nesting level + * + * @var int + */ + private $_current_template_nesting = 0; + + /** + * Cache for $resource_dir lookup + * + * @var array + */ + protected $_resource_dir = []; + + /** + * Cache for $template_dir lookup + * + * @var array + */ + protected $_template_dir = []; + + /** + * Cache for $config_dir lookup + * + * @var array + */ + protected $_config_dir = []; + + /** + * Cache for $secure_dir lookup + * + * @var array + */ + protected $_secure_dir = []; + + /** + * @param Smarty $smarty + */ + public function __construct(Smarty $smarty) { + $this->smarty = $smarty; + } + + /** + * Check if static class is trusted. + * + * @param string $class_name + * @param object $compiler compiler object + * + * @return boolean true if class is trusted + */ + public function isTrustedStaticClass($class_name, $compiler) { + if (isset($this->static_classes) + && (empty($this->static_classes) || in_array($class_name, $this->static_classes)) + ) { + return true; + } + $compiler->trigger_template_error("access to static class '{$class_name}' not allowed by security setting"); + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if static class method/property is trusted. + * + * @param string $class_name + * @param string $params + * @param object $compiler compiler object + * + * @return boolean true if class method is trusted + */ + public function isTrustedStaticClassAccess($class_name, $params, $compiler) { + if (!isset($params[2])) { + // fall back + return $this->isTrustedStaticClass($class_name, $compiler); + } + if ($params[2] === 'method') { + $allowed = $this->trusted_static_methods; + $name = substr($params[0], 0, strpos($params[0], '(')); + } else { + $allowed = $this->trusted_static_properties; + // strip '$' + $name = substr($params[0], 1); + } + if (isset($allowed)) { + if (empty($allowed)) { + // fall back + return $this->isTrustedStaticClass($class_name, $compiler); + } + if (isset($allowed[$class_name]) + && (empty($allowed[$class_name]) || in_array($name, $allowed[$class_name])) + ) { + return true; + } + } + $compiler->trigger_template_error("access to static class '{$class_name}' {$params[2]} '{$name}' not allowed by security setting"); + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if tag is trusted. + * + * @param string $tag_name + * @param object $compiler compiler object + * + * @return boolean true if tag is trusted + */ + public function isTrustedTag($tag_name, $compiler) { + $tag_name = strtolower($tag_name); + + // check for internal always required tags + if (in_array($tag_name, ['assign', 'call'])) { + return true; + } + // check security settings + if (empty($this->allowed_tags)) { + if (empty($this->disabled_tags) || !in_array($tag_name, $this->disabled_tags)) { + return true; + } else { + $compiler->trigger_template_error("tag '{$tag_name}' disabled by security setting", null, true); + } + } elseif (in_array($tag_name, $this->allowed_tags) && !in_array($tag_name, $this->disabled_tags)) { + return true; + } else { + $compiler->trigger_template_error("tag '{$tag_name}' not allowed by security setting", null, true); + } + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if special $smarty variable is trusted. + * + * @param string $var_name + * @param object $compiler compiler object + * + * @return boolean true if tag is trusted + */ + public function isTrustedSpecialSmartyVar($var_name, $compiler) { + if (!in_array($var_name, $this->disabled_special_smarty_vars)) { + return true; + } else { + $compiler->trigger_template_error( + "special variable '\$smarty.{$var_name}' not allowed by security setting", + null, + true + ); + } + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if modifier plugin is trusted. + * + * @param string $modifier_name + * @param object $compiler compiler object + * + * @return boolean true if tag is trusted + */ + public function isTrustedModifier($modifier_name, $compiler) { + // check for internal always allowed modifier + if (in_array($modifier_name, ['default'])) { + return true; + } + // check security settings + if (empty($this->allowed_modifiers)) { + if (empty($this->disabled_modifiers) || !in_array($modifier_name, $this->disabled_modifiers)) { + return true; + } else { + $compiler->trigger_template_error( + "modifier '{$modifier_name}' disabled by security setting", + null, + true + ); + } + } elseif (in_array($modifier_name, $this->allowed_modifiers) + && !in_array($modifier_name, $this->disabled_modifiers) + ) { + return true; + } else { + $compiler->trigger_template_error( + "modifier '{$modifier_name}' not allowed by security setting", + null, + true + ); + } + return false; // should not, but who knows what happens to the compiler in the future? + } + + /** + * Check if constants are enabled or trusted + * + * @param string $const constant name + * @param object $compiler compiler object + * + * @return bool + */ + public function isTrustedConstant($const, $compiler) { + if (in_array($const, ['true', 'false', 'null'])) { + return true; + } + if (!empty($this->trusted_constants)) { + if (!in_array(strtolower($const), $this->trusted_constants)) { + $compiler->trigger_template_error("Security: access to constant '{$const}' not permitted"); + return false; + } + return true; + } + if ($this->allow_constants) { + return true; + } + $compiler->trigger_template_error("Security: access to constants not permitted"); + return false; + } + + /** + * Check if stream is trusted. + * + * @param string $stream_name + * + * @return boolean true if stream is trusted + * @throws Exception if stream is not trusted + */ + public function isTrustedStream($stream_name) { + if (isset($this->streams) && (empty($this->streams) || in_array($stream_name, $this->streams))) { + return true; + } + throw new Exception("stream '{$stream_name}' not allowed by security setting"); + } + + /** + * Check if directory of file resource is trusted. + * + * @param string $filepath + * @param null|bool $isConfig + * + * @return bool true if directory is trusted + * @throws \Smarty\Exception if directory is not trusted + */ + public function isTrustedResourceDir($filepath, $isConfig = null) { + $_dir = $this->smarty->getTemplateDir(); + if ($this->_template_dir !== $_dir) { + $this->_updateResourceDir($this->_template_dir, $_dir); + $this->_template_dir = $_dir; + } + $_dir = $this->smarty->getConfigDir(); + if ($this->_config_dir !== $_dir) { + $this->_updateResourceDir($this->_config_dir, $_dir); + $this->_config_dir = $_dir; + } + if ($this->_secure_dir !== $this->secure_dir) { + $this->secure_dir = (array)$this->secure_dir; + foreach ($this->secure_dir as $k => $d) { + $this->secure_dir[$k] = $this->smarty->_realpath($d . DIRECTORY_SEPARATOR, true); + } + $this->_updateResourceDir($this->_secure_dir, $this->secure_dir); + $this->_secure_dir = $this->secure_dir; + } + $addPath = $this->_checkDir($filepath, $this->_resource_dir); + if ($addPath !== false) { + $this->_resource_dir = array_merge($this->_resource_dir, $addPath); + } + return true; + } + + /** + * Check if URI (e.g. {fetch} or {html_image}) is trusted + * To simplify things, isTrustedUri() resolves all input to "{$PROTOCOL}://{$HOSTNAME}". + * So "http://username:password@hello.world.example.org:8080/some-path?some=query-string" + * is reduced to "http://hello.world.example.org" prior to applying the patters from {@link $trusted_uri}. + * + * @param string $uri + * + * @return boolean true if URI is trusted + * @throws Exception if URI is not trusted + * @uses $trusted_uri for list of patterns to match against $uri + */ + public function isTrustedUri($uri) { + $_uri = parse_url($uri); + if (!empty($_uri['scheme']) && !empty($_uri['host'])) { + $_uri = $_uri['scheme'] . '://' . $_uri['host']; + foreach ($this->trusted_uri as $pattern) { + if (preg_match($pattern, $_uri)) { + return true; + } + } + } + throw new Exception("URI '{$uri}' not allowed by security setting"); + } + + /** + * Remove old directories and its sub folders, add new directories + * + * @param array $oldDir + * @param array $newDir + */ + private function _updateResourceDir($oldDir, $newDir) { + foreach ($oldDir as $directory) { + // $directory = $this->smarty->_realpath($directory, true); + $length = strlen($directory); + foreach ($this->_resource_dir as $dir) { + if (substr($dir, 0, $length) === $directory) { + unset($this->_resource_dir[$dir]); + } + } + } + foreach ($newDir as $directory) { + // $directory = $this->smarty->_realpath($directory, true); + $this->_resource_dir[$directory] = true; + } + } + + /** + * Check if file is inside a valid directory + * + * @param string $filepath + * @param array $dirs valid directories + * + * @return array|bool + * @throws \Smarty\Exception + */ + private function _checkDir($filepath, $dirs) { + $directory = dirname($this->smarty->_realpath($filepath, true)) . DIRECTORY_SEPARATOR; + $_directory = []; + if (!preg_match('#[\\\\/][.][.][\\\\/]#', $directory)) { + while (true) { + // test if the directory is trusted + if (isset($dirs[$directory])) { + return $_directory; + } + // abort if we've reached root + if (!preg_match('#[\\\\/][^\\\\/]+[\\\\/]$#', $directory)) { + // give up + break; + } + // remember the directory to add it to _resource_dir in case we're successful + $_directory[$directory] = true; + // bubble up one level + $directory = preg_replace('#[\\\\/][^\\\\/]+[\\\\/]$#', DIRECTORY_SEPARATOR, $directory); + } + } + // give up + throw new Exception(sprintf('Smarty Security: not trusted file path \'%s\' ', $filepath)); + } + + /** + * Loads security class and enables security + * + * @param \Smarty $smarty + * @param string|Security $security_class if a string is used, it must be class-name + * + * @return \Smarty current Smarty instance for chaining + * @throws \Smarty\Exception when an invalid class name is provided + */ + public static function enableSecurity(Smarty $smarty, $security_class) { + if ($security_class instanceof Security) { + $smarty->security_policy = $security_class; + return $smarty; + } elseif (is_object($security_class)) { + throw new Exception("Class '" . get_class($security_class) . "' must extend \\Smarty\\Security."); + } + if ($security_class === null) { + $security_class = $smarty->security_class; + } + if (!class_exists($security_class)) { + throw new Exception("Security class '$security_class' is not defined"); + } elseif ($security_class !== Security::class && !is_subclass_of($security_class, Security::class)) { + throw new Exception("Class '$security_class' must extend " . Security::class . "."); + } else { + $smarty->security_policy = new $security_class($smarty); + } + return $smarty; + } + + /** + * Start template processing + * + * @param $template + * + * @throws Exception + */ + public function startTemplate($template) { + if ($this->max_template_nesting > 0 && $this->_current_template_nesting++ >= $this->max_template_nesting) { + throw new Exception("maximum template nesting level of '{$this->max_template_nesting}' exceeded when calling '{$template->template_resource}'"); + } + } + + /** + * Exit template processing + */ + public function endTemplate() { + if ($this->max_template_nesting > 0) { + $this->_current_template_nesting--; + } + } + + /** + * Register callback functions call at start/end of template rendering + * + * @param \Smarty\Template $template + */ + public function registerCallBacks(Template $template) { + $template->startRenderCallbacks[] = [$this, 'startTemplate']; + $template->endRenderCallbacks[] = [$this, 'endTemplate']; + } +} diff --git a/src/Smarty.class.php b/src/Smarty.class.php deleted file mode 100644 index 1bfc564..0000000 --- a/src/Smarty.class.php +++ /dev/null @@ -1,1405 +0,0 @@ - - * @author Uwe Tews - * @author Rodney Rehm - * @package Smarty - */ -/** - * set SMARTY_DIR to absolute path to Smarty library files. - * Sets SMARTY_DIR only if user application has not already defined it. - */ -if (!defined('SMARTY_DIR')) { - /** - * - */ - define('SMARTY_DIR', __DIR__ . DIRECTORY_SEPARATOR); -} -/** - * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins. - * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it. - */ -if (!defined('SMARTY_SYSPLUGINS_DIR')) { - /** - * - */ - define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR); -} -if (!defined('SMARTY_PLUGINS_DIR')) { - /** - * - */ - define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DIRECTORY_SEPARATOR); -} -if (!defined('SMARTY_MBSTRING')) { - /** - * - */ - define('SMARTY_MBSTRING', function_exists('mb_get_info')); -} - -/** - * Load helper functions - */ -if (!defined('SMARTY_HELPER_FUNCTIONS_LOADED')) { - include __DIR__ . '/functions.php'; -} - -/** - * Load Smarty_Autoloader - */ -if (!class_exists('Smarty_Autoloader')) { - include __DIR__ . '/bootstrap.php'; -} - -/** - * Load always needed external class files - */ -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_extension_handler.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_variable.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_source.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_resource_base.php'; -require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_resource_file.php'; - -/** - * This is the main Smarty class - * - * @package Smarty - * - * The following methods will be dynamically loaded by the extension handler when they are called. - * They are located in a corresponding Smarty_Internal_Method_xxxx class - * - * @method int clearAllCache(int $exp_time = null, string $type = null) - * @method int clearCache(string $template_name, string $cache_id = null, string $compile_id = null, int $exp_time = null, string $type = null) - * @method int compileAllTemplates(string $extension = '.tpl', bool $force_compile = false, int $time_limit = 0, $max_errors = null) - * @method int compileAllConfig(string $extension = '.conf', bool $force_compile = false, int $time_limit = 0, $max_errors = null) - * @method int clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) - */ -class Smarty extends Smarty_Internal_TemplateBase -{ - /** - * smarty version - */ - const SMARTY_VERSION = '4.5.1'; - /** - * define variable scopes - */ - const SCOPE_LOCAL = 1; - const SCOPE_PARENT = 2; - const SCOPE_TPL_ROOT = 4; - const SCOPE_ROOT = 8; - const SCOPE_SMARTY = 16; - const SCOPE_GLOBAL = 32; - /** - * define caching modes - */ - const CACHING_OFF = 0; - const CACHING_LIFETIME_CURRENT = 1; - const CACHING_LIFETIME_SAVED = 2; - /** - * define constant for clearing cache files be saved expiration dates - */ - const CLEAR_EXPIRED = -1; - /** - * define compile check modes - */ - const COMPILECHECK_OFF = 0; - const COMPILECHECK_ON = 1; - const COMPILECHECK_CACHEMISS = 2; - /** - * define debug modes - */ - const DEBUG_OFF = 0; - const DEBUG_ON = 1; - const DEBUG_INDIVIDUAL = 2; - - /** - * filter types - */ - const FILTER_POST = 'post'; - const FILTER_PRE = 'pre'; - const FILTER_OUTPUT = 'output'; - const FILTER_VARIABLE = 'variable'; - /** - * plugin types - */ - const PLUGIN_FUNCTION = 'function'; - const PLUGIN_BLOCK = 'block'; - const PLUGIN_COMPILER = 'compiler'; - const PLUGIN_MODIFIER = 'modifier'; - const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler'; - - /** - * assigned global tpl vars - */ - public static $global_tpl_vars = array(); - - /** - * Flag denoting if Multibyte String functions are available - */ - public static $_MBSTRING = SMARTY_MBSTRING; - - /** - * The character set to adhere to (e.g. "UTF-8") - */ - public static $_CHARSET = SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1'; - - /** - * The date format to be used internally - * (accepts date() and strftime()) - */ - public static $_DATE_FORMAT = '%b %e, %Y'; - - /** - * Flag denoting if PCRE should run in UTF-8 mode - */ - public static $_UTF8_MODIFIER = 'u'; - - /** - * Flag denoting if operating system is windows - */ - public static $_IS_WINDOWS = false; - - /** - * auto literal on delimiters with whitespace - * - * @var boolean - */ - public $auto_literal = true; - - /** - * display error on not assigned variables - * - * @var boolean - */ - public $error_unassigned = false; - - /** - * look up relative file path in include_path - * - * @var boolean - */ - public $use_include_path = false; - - /** - * flag if template_dir is normalized - * - * @var bool - */ - public $_templateDirNormalized = false; - - /** - * joined template directory string used in cache keys - * - * @var string - */ - public $_joined_template_dir = null; - - /** - * flag if config_dir is normalized - * - * @var bool - */ - public $_configDirNormalized = false; - - /** - * joined config directory string used in cache keys - * - * @var string - */ - public $_joined_config_dir = null; - - /** - * default template handler - * - * @var callable - */ - public $default_template_handler_func = null; - - /** - * default config handler - * - * @var callable - */ - public $default_config_handler_func = null; - - /** - * default plugin handler - * - * @var callable - */ - public $default_plugin_handler_func = null; - - /** - * flag if template_dir is normalized - * - * @var bool - */ - public $_compileDirNormalized = false; - - /** - * flag if plugins_dir is normalized - * - * @var bool - */ - public $_pluginsDirNormalized = false; - - /** - * flag if template_dir is normalized - * - * @var bool - */ - public $_cacheDirNormalized = false; - - /** - * force template compiling? - * - * @var boolean - */ - public $force_compile = false; - - /** - * use sub dirs for compiled/cached files? - * - * @var boolean - */ - public $use_sub_dirs = false; - - /** - * allow ambiguous resources (that are made unique by the resource handler) - * - * @var boolean - */ - public $allow_ambiguous_resources = false; - - /** - * merge compiled includes - * - * @var boolean - */ - public $merge_compiled_includes = false; - - /* - * flag for behaviour when extends: resource and {extends} tag are used simultaneous - * if false disable execution of {extends} in templates called by extends resource. - * (behaviour as versions < 3.1.28) - * - * @var boolean - */ - public $extends_recursion = true; - - /** - * force cache file creation - * - * @var boolean - */ - public $force_cache = false; - - /** - * template left-delimiter - * - * @var string - */ - public $left_delimiter = "{"; - - /** - * template right-delimiter - * - * @var string - */ - public $right_delimiter = "}"; - - /** - * array of strings which shall be treated as literal by compiler - * - * @var array string - */ - public $literals = array(); - - /** - * class name - * This should be instance of Smarty_Security. - * - * @var string - * @see Smarty_Security - */ - public $security_class = 'Smarty_Security'; - - /** - * implementation of security class - * - * @var Smarty_Security - */ - public $security_policy = null; - - /** - * controls if the php template file resource is allowed - * - * @var bool - */ - public $allow_php_templates = false; - - /** - * debug mode - * Setting this to true enables the debug-console. - * - * @var boolean - */ - public $debugging = false; - - /** - * This determines if debugging is enable-able from the browser. - *
    - *
  • NONE => no debugging control allowed
  • - *
  • URL => enable debugging when SMARTY_DEBUG is found in the URL.
  • - *
- * - * @var string - */ - public $debugging_ctrl = 'NONE'; - - /** - * Name of debugging URL-param. - * Only used when $debugging_ctrl is set to 'URL'. - * The name of the URL-parameter that activates debugging. - * - * @var string - */ - public $smarty_debug_id = 'SMARTY_DEBUG'; - - /** - * Path of debug template. - * - * @var string - */ - public $debug_tpl = null; - - /** - * When set, smarty uses this value as error_reporting-level. - * - * @var int - */ - public $error_reporting = null; - - /** - * Controls whether variables with the same name overwrite each other. - * - * @var boolean - */ - public $config_overwrite = true; - - /** - * Controls whether config values of on/true/yes and off/false/no get converted to boolean. - * - * @var boolean - */ - public $config_booleanize = true; - - /** - * Controls whether hidden config sections/vars are read from the file. - * - * @var boolean - */ - public $config_read_hidden = false; - - /** - * locking concurrent compiles - * - * @var boolean - */ - public $compile_locking = true; - - /** - * Controls whether cache resources should use locking mechanism - * - * @var boolean - */ - public $cache_locking = false; - - /** - * seconds to wait for acquiring a lock before ignoring the write lock - * - * @var float - */ - public $locking_timeout = 10; - - /** - * resource type used if none given - * Must be an valid key of $registered_resources. - * - * @var string - */ - public $default_resource_type = 'file'; - - /** - * caching type - * Must be an element of $cache_resource_types. - * - * @var string - */ - public $caching_type = 'file'; - - /** - * config type - * - * @var string - */ - public $default_config_type = 'file'; - - /** - * check If-Modified-Since headers - * - * @var boolean - */ - public $cache_modified_check = false; - - /** - * registered plugins - * - * @var array - */ - public $registered_plugins = array(); - - /** - * registered objects - * - * @var array - */ - public $registered_objects = array(); - - /** - * registered classes - * - * @var array - */ - public $registered_classes = array(); - - /** - * registered filters - * - * @var array - */ - public $registered_filters = array(); - - /** - * registered resources - * - * @var array - */ - public $registered_resources = array(); - - /** - * registered cache resources - * - * @var array - */ - public $registered_cache_resources = array(); - - /** - * autoload filter - * - * @var array - */ - public $autoload_filters = array(); - - /** - * default modifier - * - * @var array - */ - public $default_modifiers = array(); - - /** - * autoescape variable output - * - * @var boolean - */ - public $escape_html = false; - - /** - * start time for execution time calculation - * - * @var int - */ - public $start_time = 0; - - /** - * required by the compiler for BC - * - * @var string - */ - public $_current_file = null; - - /** - * internal flag to enable parser debugging - * - * @var bool - */ - public $_parserdebug = false; - - /** - * This object type (Smarty = 1, template = 2, data = 4) - * - * @var int - */ - public $_objType = 1; - - /** - * Debug object - * - * @var Smarty_Internal_Debug - */ - public $_debug = null; - - /** - * template directory - * - * @var array - */ - protected $template_dir = array('./templates/'); - - /** - * flags for normalized template directory entries - * - * @var array - */ - protected $_processedTemplateDir = array(); - - /** - * config directory - * - * @var array - */ - protected $config_dir = array('./configs/'); - - /** - * flags for normalized template directory entries - * - * @var array - */ - protected $_processedConfigDir = array(); - - /** - * compile directory - * - * @var string - */ - protected $compile_dir = './templates_c/'; - - /** - * plugins directory - * - * @var array - */ - protected $plugins_dir = array(); - - /** - * cache directory - * - * @var string - */ - protected $cache_dir = './cache/'; - - /** - * removed properties - * - * @var string[] - */ - protected $obsoleteProperties = array( - 'resource_caching', 'template_resource_caching', 'direct_access_security', - '_dir_perms', '_file_perms', 'plugin_search_order', - 'inheritance_merge_compiled_includes', 'resource_cache_mode', - ); - - /** - * List of private properties which will call getter/setter on a direct access - * - * @var string[] - */ - protected $accessMap = array( - 'template_dir' => 'TemplateDir', 'config_dir' => 'ConfigDir', - 'plugins_dir' => 'PluginsDir', 'compile_dir' => 'CompileDir', - 'cache_dir' => 'CacheDir', - ); - - /** - * PHP7 Compatibility mode - * @var bool - */ - private $isMutingUndefinedOrNullWarnings = false; - - /** - * Initialize new Smarty object - */ - public function __construct() - { - $this->_clearTemplateCache(); - parent::__construct(); - if (is_callable('mb_internal_encoding')) { - mb_internal_encoding(Smarty::$_CHARSET); - } - $this->start_time = microtime(true); - if (isset($_SERVER[ 'SCRIPT_NAME' ])) { - Smarty::$global_tpl_vars[ 'SCRIPT_NAME' ] = new Smarty_Variable($_SERVER[ 'SCRIPT_NAME' ]); - } - // Check if we're running on windows - Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; - // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8 - if (Smarty::$_CHARSET !== 'UTF-8') { - Smarty::$_UTF8_MODIFIER = ''; - } - } - - /** - * Check if a template resource exists - * - * @param string $resource_name template name - * - * @return bool status - * @throws \SmartyException - */ - public function templateExists($resource_name) - { - // create source object - $source = Smarty_Template_Source::load(null, $this, $resource_name); - return $source->exists; - } - - /** - * Loads security class and enables security - * - * @param string|Smarty_Security $security_class if a string is used, it must be class-name - * - * @return Smarty current Smarty instance for chaining - * @throws \SmartyException - */ - public function enableSecurity($security_class = null) - { - Smarty_Security::enableSecurity($this, $security_class); - return $this; - } - - /** - * Disable security - * - * @return Smarty current Smarty instance for chaining - */ - public function disableSecurity() - { - $this->security_policy = null; - return $this; - } - - /** - * Add template directory(s) - * - * @param string|array $template_dir directory(s) of template sources - * @param string $key of the array element to assign the template dir to - * @param bool $isConfig true for config_dir - * - * @return Smarty current Smarty instance for chaining - */ - public function addTemplateDir($template_dir, $key = null, $isConfig = false) - { - if ($isConfig) { - $processed = &$this->_processedConfigDir; - $dir = &$this->config_dir; - $this->_configDirNormalized = false; - } else { - $processed = &$this->_processedTemplateDir; - $dir = &$this->template_dir; - $this->_templateDirNormalized = false; - } - if (is_array($template_dir)) { - foreach ($template_dir as $k => $v) { - if (is_int($k)) { - // indexes are not merged but appended - $dir[] = $v; - } else { - // string indexes are overridden - $dir[ $k ] = $v; - unset($processed[ $key ]); - } - } - } else { - if ($key !== null) { - // override directory at specified index - $dir[ $key ] = $template_dir; - unset($processed[ $key ]); - } else { - // append new directory - $dir[] = $template_dir; - } - } - return $this; - } - - /** - * Get template directories - * - * @param mixed $index index of directory to get, null to get all - * @param bool $isConfig true for config_dir - * - * @return array|string list of template directories, or directory of $index - */ - public function getTemplateDir($index = null, $isConfig = false) - { - if ($isConfig) { - $dir = &$this->config_dir; - } else { - $dir = &$this->template_dir; - } - if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) { - $this->_normalizeTemplateConfig($isConfig); - } - if ($index !== null) { - return isset($dir[ $index ]) ? $dir[ $index ] : null; - } - return $dir; - } - - /** - * Set template directory - * - * @param string|array $template_dir directory(s) of template sources - * @param bool $isConfig true for config_dir - * - * @return \Smarty current Smarty instance for chaining - */ - public function setTemplateDir($template_dir, $isConfig = false) - { - if ($isConfig) { - $this->config_dir = array(); - $this->_processedConfigDir = array(); - } else { - $this->template_dir = array(); - $this->_processedTemplateDir = array(); - } - $this->addTemplateDir($template_dir, null, $isConfig); - return $this; - } - - /** - * Add config directory(s) - * - * @param string|array $config_dir directory(s) of config sources - * @param mixed $key key of the array element to assign the config dir to - * - * @return Smarty current Smarty instance for chaining - */ - public function addConfigDir($config_dir, $key = null) - { - return $this->addTemplateDir($config_dir, $key, true); - } - - /** - * Get config directory - * - * @param mixed $index index of directory to get, null to get all - * - * @return array configuration directory - */ - public function getConfigDir($index = null) - { - return $this->getTemplateDir($index, true); - } - - /** - * Set config directory - * - * @param $config_dir - * - * @return Smarty current Smarty instance for chaining - */ - public function setConfigDir($config_dir) - { - return $this->setTemplateDir($config_dir, true); - } - - /** - * Adds directory of plugin files - * - * @param null|array|string $plugins_dir - * - * @return Smarty current Smarty instance for chaining - */ - public function addPluginsDir($plugins_dir) - { - if (empty($this->plugins_dir)) { - $this->plugins_dir[] = SMARTY_PLUGINS_DIR; - } - $this->plugins_dir = array_merge($this->plugins_dir, (array)$plugins_dir); - $this->_pluginsDirNormalized = false; - return $this; - } - - /** - * Get plugin directories - * - * @return array list of plugin directories - */ - public function getPluginsDir() - { - if (empty($this->plugins_dir)) { - $this->plugins_dir[] = SMARTY_PLUGINS_DIR; - $this->_pluginsDirNormalized = false; - } - if (!$this->_pluginsDirNormalized) { - if (!is_array($this->plugins_dir)) { - $this->plugins_dir = (array)$this->plugins_dir; - } - foreach ($this->plugins_dir as $k => $v) { - $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v ?? '', '/\\') . DIRECTORY_SEPARATOR, true); - } - $this->_cache[ 'plugin_files' ] = array(); - $this->_pluginsDirNormalized = true; - } - return $this->plugins_dir; - } - - /** - * Set plugins directory - * - * @param string|array $plugins_dir directory(s) of plugins - * - * @return Smarty current Smarty instance for chaining - */ - public function setPluginsDir($plugins_dir) - { - $this->plugins_dir = (array)$plugins_dir; - $this->_pluginsDirNormalized = false; - return $this; - } - - /** - * Get compiled directory - * - * @return string path to compiled templates - */ - public function getCompileDir() - { - if (!$this->_compileDirNormalized) { - $this->_normalizeDir('compile_dir', $this->compile_dir); - $this->_compileDirNormalized = true; - } - return $this->compile_dir; - } - - /** - * - * @param string $compile_dir directory to store compiled templates in - * - * @return Smarty current Smarty instance for chaining - */ - public function setCompileDir($compile_dir) - { - $this->_normalizeDir('compile_dir', $compile_dir); - $this->_compileDirNormalized = true; - return $this; - } - - /** - * Get cache directory - * - * @return string path of cache directory - */ - public function getCacheDir() - { - if (!$this->_cacheDirNormalized) { - $this->_normalizeDir('cache_dir', $this->cache_dir); - $this->_cacheDirNormalized = true; - } - return $this->cache_dir; - } - - /** - * Set cache directory - * - * @param string $cache_dir directory to store cached templates in - * - * @return Smarty current Smarty instance for chaining - */ - public function setCacheDir($cache_dir) - { - $this->_normalizeDir('cache_dir', $cache_dir); - $this->_cacheDirNormalized = true; - return $this; - } - - /** - * creates a template object - * - * @param string $template the resource handle of the template file - * @param mixed $cache_id cache id to be used with this template - * @param mixed $compile_id compile id to be used with this template - * @param object $parent next higher level of Smarty variables - * @param boolean $do_clone flag is Smarty object shall be cloned - * - * @return \Smarty_Internal_Template template object - * @throws \SmartyException - */ - public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true) - { - if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) { - $parent = $cache_id; - $cache_id = null; - } - if ($parent !== null && is_array($parent)) { - $data = $parent; - $parent = null; - } else { - $data = null; - } - if (!$this->_templateDirNormalized) { - $this->_normalizeTemplateConfig(false); - } - $_templateId = $this->_getTemplateId($template, $cache_id, $compile_id); - $tpl = null; - if ($this->caching && isset(Smarty_Internal_Template::$isCacheTplObj[ $_templateId ])) { - $tpl = $do_clone ? clone Smarty_Internal_Template::$isCacheTplObj[ $_templateId ] : - Smarty_Internal_Template::$isCacheTplObj[ $_templateId ]; - $tpl->inheritance = null; - $tpl->tpl_vars = $tpl->config_vars = array(); - } elseif (!$do_clone && isset(Smarty_Internal_Template::$tplObjCache[ $_templateId ])) { - $tpl = clone Smarty_Internal_Template::$tplObjCache[ $_templateId ]; - $tpl->inheritance = null; - $tpl->tpl_vars = $tpl->config_vars = array(); - } else { - /* @var Smarty_Internal_Template $tpl */ - $tpl = new $this->template_class($template, $this, null, $cache_id, $compile_id, null, null); - $tpl->templateId = $_templateId; - } - if ($do_clone) { - $tpl->smarty = clone $tpl->smarty; - } - $tpl->parent = $parent ? $parent : $this; - // fill data if present - if (!empty($data) && is_array($data)) { - // set up variable values - foreach ($data as $_key => $_val) { - $tpl->tpl_vars[ $_key ] = new Smarty_Variable($_val); - } - } - if ($this->debugging || $this->debugging_ctrl === 'URL') { - $tpl->smarty->_debug = new Smarty_Internal_Debug(); - // check URL debugging control - if (!$this->debugging && $this->debugging_ctrl === 'URL') { - $tpl->smarty->_debug->debugUrl($tpl->smarty); - } - } - return $tpl; - } - - /** - * Takes unknown classes and loads plugin files for them - * class name format: Smarty_PluginType_PluginName - * plugin filename format: plugintype.pluginname.php - * - * @param string $plugin_name class plugin name to load - * @param bool $check check if already loaded - * - * @return string |boolean filepath of loaded file or false - * @throws \SmartyException - */ - public function loadPlugin($plugin_name, $check = true) - { - return $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check); - } - - /** - * Get unique template id - * - * @param string $template_name - * @param null|mixed $cache_id - * @param null|mixed $compile_id - * @param null $caching - * @param \Smarty_Internal_Template $template - * - * @return string - * @throws \SmartyException - */ - public function _getTemplateId( - $template_name, - $cache_id = null, - $compile_id = null, - $caching = null, - Smarty_Internal_Template $template = null - ) { - $template_name = (strpos($template_name, ':') === false) ? "{$this->default_resource_type}:{$template_name}" : - $template_name; - $cache_id = $cache_id === null ? $this->cache_id : $cache_id; - $compile_id = $compile_id === null ? $this->compile_id : $compile_id; - $caching = (int)($caching === null ? $this->caching : $caching); - if ((isset($template) && strpos($template_name, ':.') !== false) || $this->allow_ambiguous_resources) { - $_templateId = - Smarty_Resource::getUniqueTemplateName((isset($template) ? $template : $this), $template_name) . - "#{$cache_id}#{$compile_id}#{$caching}"; - } else { - $_templateId = $this->_joined_template_dir . "#{$template_name}#{$cache_id}#{$compile_id}#{$caching}"; - } - if (isset($_templateId[ 150 ])) { - $_templateId = sha1($_templateId); - } - return $_templateId; - } - - /** - * Normalize path - * - remove /./ and /../ - * - make it absolute if required - * - * @param string $path file path - * @param bool $realpath if true - convert to absolute - * false - convert to relative - * null - keep as it is but - * remove /./ /../ - * - * @return string - */ - public function _realpath($path, $realpath = null) - { - $nds = array('/' => '\\', '\\' => '/'); - preg_match( - '%^(?(?:[[:alpha:]]:[\\\\/]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?(.*))$%u', - $path, - $parts - ); - $path = $parts[ 'path' ]; - if ($parts[ 'root' ] === '\\') { - $parts[ 'root' ] = substr(getcwd(), 0, 2) . $parts[ 'root' ]; - } else { - if ($realpath !== null && !$parts[ 'root' ]) { - $path = getcwd() . DIRECTORY_SEPARATOR . $path; - } - } - // normalize DIRECTORY_SEPARATOR - $path = str_replace($nds[ DIRECTORY_SEPARATOR ], DIRECTORY_SEPARATOR, $path); - $parts[ 'root' ] = str_replace($nds[ DIRECTORY_SEPARATOR ], DIRECTORY_SEPARATOR, $parts[ 'root' ]); - do { - $path = preg_replace( - array('#[\\\\/]{2}#', '#[\\\\/][.][\\\\/]#', '#[\\\\/]([^\\\\/.]+)[\\\\/][.][.][\\\\/]#'), - DIRECTORY_SEPARATOR, - $path, - -1, - $count - ); - } while ($count > 0); - return $realpath !== false ? $parts[ 'root' ] . $path : str_ireplace(getcwd(), '.', $parts[ 'root' ] . $path); - } - - /** - * Empty template objects cache - */ - public function _clearTemplateCache() - { - Smarty_Internal_Template::$isCacheTplObj = array(); - Smarty_Internal_Template::$tplObjCache = array(); - } - - /** - * @param boolean $use_sub_dirs - */ - public function setUseSubDirs($use_sub_dirs) - { - $this->use_sub_dirs = $use_sub_dirs; - } - - /** - * @param int $error_reporting - */ - public function setErrorReporting($error_reporting) - { - $this->error_reporting = $error_reporting; - } - - /** - * @param boolean $escape_html - */ - public function setEscapeHtml($escape_html) - { - $this->escape_html = $escape_html; - } - - /** - * Return auto_literal flag - * - * @return boolean - */ - public function getAutoLiteral() - { - return $this->auto_literal; - } - - /** - * Set auto_literal flag - * - * @param boolean $auto_literal - */ - public function setAutoLiteral($auto_literal = true) - { - $this->auto_literal = $auto_literal; - } - - /** - * @param boolean $force_compile - */ - public function setForceCompile($force_compile) - { - $this->force_compile = $force_compile; - } - - /** - * @param boolean $merge_compiled_includes - */ - public function setMergeCompiledIncludes($merge_compiled_includes) - { - $this->merge_compiled_includes = $merge_compiled_includes; - } - - /** - * Get left delimiter - * - * @return string - */ - public function getLeftDelimiter() - { - return $this->left_delimiter; - } - - /** - * Set left delimiter - * - * @param string $left_delimiter - */ - public function setLeftDelimiter($left_delimiter) - { - $this->left_delimiter = $left_delimiter; - } - - /** - * Get right delimiter - * - * @return string $right_delimiter - */ - public function getRightDelimiter() - { - return $this->right_delimiter; - } - - /** - * Set right delimiter - * - * @param string - */ - public function setRightDelimiter($right_delimiter) - { - $this->right_delimiter = $right_delimiter; - } - - /** - * @param boolean $debugging - */ - public function setDebugging($debugging) - { - $this->debugging = $debugging; - } - - /** - * @param boolean $config_overwrite - */ - public function setConfigOverwrite($config_overwrite) - { - $this->config_overwrite = $config_overwrite; - } - - /** - * @param boolean $config_booleanize - */ - public function setConfigBooleanize($config_booleanize) - { - $this->config_booleanize = $config_booleanize; - } - - /** - * @param boolean $config_read_hidden - */ - public function setConfigReadHidden($config_read_hidden) - { - $this->config_read_hidden = $config_read_hidden; - } - - /** - * @param boolean $compile_locking - */ - public function setCompileLocking($compile_locking) - { - $this->compile_locking = $compile_locking; - } - - /** - * @param string $default_resource_type - */ - public function setDefaultResourceType($default_resource_type) - { - $this->default_resource_type = $default_resource_type; - } - - /** - * @param string $caching_type - */ - public function setCachingType($caching_type) - { - $this->caching_type = $caching_type; - } - - /** - * Test install - * - * @param null $errors - */ - public function testInstall(&$errors = null) - { - Smarty_Internal_TestInstall::testInstall($this, $errors); - } - - /** - * Get Smarty object - * - * @return Smarty - */ - public function _getSmartyObj() - { - return $this; - } - - /** - * <> Generic getter. - * Calls the appropriate getter function. - * Issues an E_USER_NOTICE if no valid getter is found. - * - * @param string $name property name - * - * @return mixed - */ - public function __get($name) - { - if (isset($this->accessMap[ $name ])) { - $method = 'get' . $this->accessMap[ $name ]; - return $this->{$method}(); - } elseif (isset($this->_cache[ $name ])) { - return $this->_cache[ $name ]; - } elseif (in_array($name, $this->obsoleteProperties)) { - return null; - } else { - trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE); - } - return null; - } - - /** - * <> Generic setter. - * Calls the appropriate setter function. - * Issues an E_USER_NOTICE if no valid setter is found. - * - * @param string $name property name - * @param mixed $value parameter passed to setter - * - */ - public function __set($name, $value) - { - if (isset($this->accessMap[ $name ])) { - $method = 'set' . $this->accessMap[ $name ]; - $this->{$method}($value); - } elseif (in_array($name, $this->obsoleteProperties)) { - return; - } elseif (is_object($value) && method_exists($value, $name)) { - $this->$name = $value; - } else { - trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE); - } - } - - /** - * Normalize and set directory string - * - * @param string $dirName cache_dir or compile_dir - * @param string $dir filepath of folder - */ - private function _normalizeDir($dirName, $dir) - { - $this->{$dirName} = $this->_realpath(rtrim($dir ?? '', "/\\") . DIRECTORY_SEPARATOR, true); - } - - /** - * Normalize template_dir or config_dir - * - * @param bool $isConfig true for config_dir - */ - private function _normalizeTemplateConfig($isConfig) - { - if ($isConfig) { - $processed = &$this->_processedConfigDir; - $dir = &$this->config_dir; - } else { - $processed = &$this->_processedTemplateDir; - $dir = &$this->template_dir; - } - if (!is_array($dir)) { - $dir = (array)$dir; - } - foreach ($dir as $k => $v) { - if (!isset($processed[ $k ])) { - $dir[ $k ] = $v = $this->_realpath(rtrim($v ?? '', "/\\") . DIRECTORY_SEPARATOR, true); - $processed[ $k ] = true; - } - } - $isConfig ? $this->_configDirNormalized = true : $this->_templateDirNormalized = true; - $isConfig ? $this->_joined_config_dir = join('#', $this->config_dir) : - $this->_joined_template_dir = join('#', $this->template_dir); - } - - /** - * Mutes errors for "undefined index", "undefined array key" and "trying to read property of null". - * - * @void - */ - public function muteUndefinedOrNullWarnings(): void { - $this->isMutingUndefinedOrNullWarnings = true; - } - - /** - * Indicates if Smarty will mute errors for "undefined index", "undefined array key" and "trying to read property of null". - * @bool - */ - public function isMutingUndefinedOrNullWarnings(): bool { - return $this->isMutingUndefinedOrNullWarnings; - } - -} diff --git a/src/Smarty.php b/src/Smarty.php new file mode 100644 index 0000000..cde06cc --- /dev/null +++ b/src/Smarty.php @@ -0,0 +1,2240 @@ + + * @author Uwe Tews + * @author Rodney Rehm + * @author Simon Wisselink + */ + +/** + * This is the main Smarty class + */ +class Smarty extends \Smarty\TemplateBase { + + /** + * smarty version + */ + const SMARTY_VERSION = '5.3.1'; + + /** + * define caching modes + */ + const CACHING_OFF = 0; + const CACHING_LIFETIME_CURRENT = 1; + const CACHING_LIFETIME_SAVED = 2; + /** + * define constant for clearing cache files be saved expiration dates + */ + const CLEAR_EXPIRED = -1; + /** + * define compile check modes + */ + const COMPILECHECK_OFF = 0; + const COMPILECHECK_ON = 1; + /** + * filter types + */ + const FILTER_POST = 'post'; + const FILTER_PRE = 'pre'; + const FILTER_OUTPUT = 'output'; + const FILTER_VARIABLE = 'variable'; + /** + * plugin types + */ + const PLUGIN_FUNCTION = 'function'; + const PLUGIN_BLOCK = 'block'; + const PLUGIN_COMPILER = 'compiler'; + const PLUGIN_MODIFIER = 'modifier'; + const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler'; + + /** + * The character set to adhere to (defaults to "UTF-8") + */ + public static $_CHARSET = 'UTF-8'; + + /** + * The date format to be used internally + * (accepts date() and strftime()) + */ + public static $_DATE_FORMAT = '%b %e, %Y'; + + /** + * Flag denoting if PCRE should run in UTF-8 mode + */ + public static $_UTF8_MODIFIER = 'u'; + + /** + * Flag denoting if operating system is windows + */ + public static $_IS_WINDOWS = false; + + /** + * auto literal on delimiters with whitespace + * + * @var boolean + */ + public $auto_literal = true; + + /** + * display error on not assigned variables + * + * @var boolean + */ + public $error_unassigned = false; + + /** + * flag if template_dir is normalized + * + * @var bool + */ + public $_templateDirNormalized = false; + + /** + * joined template directory string used in cache keys + * + * @var string + */ + public $_joined_template_dir = null; + + /** + * flag if config_dir is normalized + * + * @var bool + */ + public $_configDirNormalized = false; + + /** + * joined config directory string used in cache keys + * + * @var string + */ + public $_joined_config_dir = null; + + /** + * default template handler + * + * @var callable + */ + public $default_template_handler_func = null; + + /** + * default config handler + * + * @var callable + */ + public $default_config_handler_func = null; + + /** + * default plugin handler + * + * @var callable + */ + private $default_plugin_handler_func = null; + + /** + * flag if template_dir is normalized + * + * @var bool + */ + public $_compileDirNormalized = false; + + /** + * flag if template_dir is normalized + * + * @var bool + */ + public $_cacheDirNormalized = false; + + /** + * force template compiling? + * + * @var boolean + */ + public $force_compile = false; + + /** + * use sub dirs for compiled/cached files? + * + * @var boolean + */ + public $use_sub_dirs = false; + + /** + * merge compiled includes + * + * @var boolean + */ + public $merge_compiled_includes = false; + + /** + * force cache file creation + * + * @var boolean + */ + public $force_cache = false; + + /** + * template left-delimiter + * + * @var string + */ + private $left_delimiter = "{"; + + /** + * template right-delimiter + * + * @var string + */ + private $right_delimiter = "}"; + + /** + * array of strings which shall be treated as literal by compiler + * + * @var array string + */ + public $literals = []; + + /** + * class name + * This should be instance of \Smarty\Security. + * + * @var string + * @see \Smarty\Security + */ + public $security_class = \Smarty\Security::class; + + /** + * implementation of security class + * + * @var \Smarty\Security + */ + public $security_policy = null; + + /** + * debug mode + * Setting this to true enables the debug-console. Setting it to 2 enables individual Debug Console window by + * template name. + * + * @var boolean|int + */ + public $debugging = false; + + /** + * This determines if debugging is enable-able from the browser. + *
    + *
  • NONE => no debugging control allowed
  • + *
  • URL => enable debugging when SMARTY_DEBUG is found in the URL.
  • + *
+ * + * @var string + */ + public $debugging_ctrl = 'NONE'; + + /** + * Name of debugging URL-param. + * Only used when $debugging_ctrl is set to 'URL'. + * The name of the URL-parameter that activates debugging. + * + * @var string + */ + public $smarty_debug_id = 'SMARTY_DEBUG'; + + /** + * Path of debug template. + * + * @var string + */ + public $debug_tpl = null; + + /** + * When set, smarty uses this value as error_reporting-level. + * + * @var int + */ + public $error_reporting = null; + + /** + * Controls whether variables with the same name overwrite each other. + * + * @var boolean + */ + public $config_overwrite = true; + + /** + * Controls whether config values of on/true/yes and off/false/no get converted to boolean. + * + * @var boolean + */ + public $config_booleanize = true; + + /** + * Controls whether hidden config sections/vars are read from the file. + * + * @var boolean + */ + public $config_read_hidden = false; + + /** + * locking concurrent compiles + * + * @var boolean + */ + public $compile_locking = true; + + /** + * Controls whether cache resources should use locking mechanism + * + * @var boolean + */ + public $cache_locking = false; + + /** + * seconds to wait for acquiring a lock before ignoring the write lock + * + * @var float + */ + public $locking_timeout = 10; + + /** + * resource type used if none given + * Must be a valid key of $registered_resources. + * + * @var string + */ + public $default_resource_type = 'file'; + + /** + * cache resource + * Must be a subclass of \Smarty\Cacheresource\Base + * + * @var \Smarty\Cacheresource\Base + */ + private $cacheResource; + + /** + * config type + * + * @var string + */ + public $default_config_type = 'file'; + + /** + * check If-Modified-Since headers + * + * @var boolean + */ + public $cache_modified_check = false; + + /** + * registered plugins + * + * @var array + */ + public $registered_plugins = []; + + /** + * registered objects + * + * @var array + */ + public $registered_objects = []; + + /** + * registered classes + * + * @var array + */ + public $registered_classes = []; + + /** + * registered resources + * + * @var array + */ + public $registered_resources = []; + + /** + * registered cache resources + * + * @var array + * @deprecated since 5.0 + */ + private $registered_cache_resources = []; + + /** + * default modifier + * + * @var array + */ + public $default_modifiers = []; + + /** + * autoescape variable output + * + * @var boolean + */ + public $escape_html = false; + + /** + * start time for execution time calculation + * + * @var int + */ + public $start_time = 0; + + /** + * internal flag to enable parser debugging + * + * @var bool + */ + public $_parserdebug = false; + + /** + * Debug object + * + * @var \Smarty\Debug + */ + public $_debug = null; + + /** + * template directory + * + * @var array + */ + protected $template_dir = ['./templates/']; + + /** + * flags for normalized template directory entries + * + * @var array + */ + protected $_processedTemplateDir = []; + + /** + * config directory + * + * @var array + */ + protected $config_dir = ['./configs/']; + + /** + * flags for normalized template directory entries + * + * @var array + */ + protected $_processedConfigDir = []; + + /** + * compile directory + * + * @var string + */ + protected $compile_dir = './templates_c/'; + + /** + * cache directory + * + * @var string + */ + protected $cache_dir = './cache/'; + + /** + * PHP7 Compatibility mode + * + * @var bool + */ + private $isMutingUndefinedOrNullWarnings = false; + + /** + * Cache of loaded resource handlers. + * + * @var array + */ + public $_resource_handlers = []; + + /** + * Cache of loaded cacheresource handlers. + * + * @var array + */ + public $_cacheresource_handlers = []; + + /** + * List of extensions + * + * @var ExtensionInterface[] + */ + private $extensions = []; + /** + * @var BCPluginsAdapter + */ + private $BCPluginsAdapter; + + /** + * Initialize new Smarty object + */ + public function __construct() { + + $this->start_time = microtime(true); + // Check if we're running on Windows + \Smarty\Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; + // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8 + if (\Smarty\Smarty::$_CHARSET !== 'UTF-8') { + \Smarty\Smarty::$_UTF8_MODIFIER = ''; + } + + $this->BCPluginsAdapter = new BCPluginsAdapter($this); + + $this->extensions[] = new CoreExtension(); + $this->extensions[] = new DefaultExtension(); + // print "EXT:
" . print_r($this->extensions, true) . "
>"; + $this->extensions[] = $this->BCPluginsAdapter; + + $this->cacheResource = new File(); + } + + /** + * Load an additional extension. + * + * @return void + */ + public function addExtension(ExtensionInterface $extension) { + $this->extensions[] = $extension; + } + + /** + * Returns all loaded extensions + * + * @return array|ExtensionInterface[] + */ + public function getExtensions(): array { + return $this->extensions; + } + + /** + * Replace the entire list extensions, allowing you to determine the exact order of the extensions. + * + * @param ExtensionInterface[] $extensions + * + * @return void + */ + public function setExtensions(array $extensions): void { + $this->extensions = $extensions; + } + + /** + * Check if a template resource exists + * + * @param string $resource_name template name + * + * @return bool status + * @throws \Smarty\Exception + */ + public function templateExists($resource_name) { + // create source object + $source = Template\Source::load(null, $this, $resource_name); + return $source->exists; + } + + /** + * Loads security class and enables security + * + * @param string|\Smarty\Security $security_class if a string is used, it must be class-name + * + * @return static current Smarty instance for chaining + * @throws \Smarty\Exception + */ + public function enableSecurity($security_class = null) { + \Smarty\Security::enableSecurity($this, $security_class); + return $this; + } + + /** + * Disable security + * + * @return static current Smarty instance for chaining + */ + public function disableSecurity() { + $this->security_policy = null; + return $this; + } + + /** + * Add template directory(s) + * + * @param string|array $template_dir directory(s) of template sources + * @param string $key of the array element to assign the template dir to + * @param bool $isConfig true for config_dir + * + * @return static current Smarty instance for chaining + */ + public function addTemplateDir($template_dir, $key = null, $isConfig = false) { + if ($isConfig) { + $processed = &$this->_processedConfigDir; + $dir = &$this->config_dir; + $this->_configDirNormalized = false; + } else { + $processed = &$this->_processedTemplateDir; + $dir = &$this->template_dir; + $this->_templateDirNormalized = false; + } + if (is_array($template_dir)) { + foreach ($template_dir as $k => $v) { + if (is_int($k)) { + // indexes are not merged but appended + $dir[] = $v; + } else { + // string indexes are overridden + $dir[$k] = $v; + unset($processed[$key]); + } + } + } else { + if ($key !== null) { + // override directory at specified index + $dir[$key] = $template_dir; + unset($processed[$key]); + } else { + // append new directory + $dir[] = $template_dir; + } + } + return $this; + } + + /** + * Get template directories + * + * @param mixed $index index of directory to get, null to get all + * @param bool $isConfig true for config_dir + * + * @return array|string list of template directories, or directory of $index + */ + public function getTemplateDir($index = null, $isConfig = false) { + if ($isConfig) { + $dir = &$this->config_dir; + } else { + $dir = &$this->template_dir; + } + if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) { + $this->_normalizeTemplateConfig($isConfig); + } + if ($index !== null) { + return isset($dir[$index]) ? $dir[$index] : null; + } + return $dir; + } + + /** + * Set template directory + * + * @param string|array $template_dir directory(s) of template sources + * @param bool $isConfig true for config_dir + * + * @return static current Smarty instance for chaining + */ + public function setTemplateDir($template_dir, $isConfig = false) { + if ($isConfig) { + $this->config_dir = []; + $this->_processedConfigDir = []; + } else { + $this->template_dir = []; + $this->_processedTemplateDir = []; + } + $this->addTemplateDir($template_dir, null, $isConfig); + return $this; + } + + /** + * Adds a template directory before any existing directoires + * + * @param string $new_template_dir directory of template sources + * @param bool $is_config true for config_dir + * + * @return static current Smarty instance for chaining + */ + public function prependTemplateDir($new_template_dir, $is_config = false) { + $current_template_dirs = $is_config ? $this->config_dir : $this->template_dir; + array_unshift($current_template_dirs, $new_template_dir); + $this->setTemplateDir($current_template_dirs, $is_config); + return $this; + } + + /** + * Add config directory(s) + * + * @param string|array $config_dir directory(s) of config sources + * @param mixed $key key of the array element to assign the config dir to + * + * @return static current Smarty instance for chaining + */ + public function addConfigDir($config_dir, $key = null) { + return $this->addTemplateDir($config_dir, $key, true); + } + + /** + * Get config directory + * + * @param mixed $index index of directory to get, null to get all + * + * @return array configuration directory + */ + public function getConfigDir($index = null) { + return $this->getTemplateDir($index, true); + } + + /** + * Set config directory + * + * @param $config_dir + * + * @return static current Smarty instance for chaining + */ + public function setConfigDir($config_dir) { + return $this->setTemplateDir($config_dir, true); + } + + /** + * Registers plugin to be used in templates + * + * @param string $type plugin type + * @param string $name name of template tag + * @param callable $callback PHP callback to register + * @param bool $cacheable if true (default) this function is cache able + * + * @return $this + * @throws \Smarty\Exception + * + * @api Smarty::registerPlugin() + */ + public function registerPlugin($type, $name, $callback, $cacheable = true) { + if (isset($this->registered_plugins[$type][$name])) { + throw new Exception("Plugin tag '{$name}' already registered"); + } elseif (!is_callable($callback) && !class_exists($callback)) { + throw new Exception("Plugin '{$name}' not callable"); + } else { + $this->registered_plugins[$type][$name] = [$callback, (bool)$cacheable]; + } + return $this; + } + + /** + * Returns plugin previously registered using ::registerPlugin as a numerical array as follows or null if not found: + * [ + * 0 => the callback + * 1 => (bool) $cacheable + * 2 => (array) $cache_attr + * ] + * + * @param string $type plugin type + * @param string $name name of template tag + * + * @return array|null + * + * @api Smarty::unregisterPlugin() + */ + public function getRegisteredPlugin($type, $name): ?array { + if (isset($this->registered_plugins[$type][$name])) { + return $this->registered_plugins[$type][$name]; + } + return null; + } + + /** + * Unregisters plugin previously registered using ::registerPlugin + * + * @param string $type plugin type + * @param string $name name of template tag + * + * @return $this + * + * @api Smarty::unregisterPlugin() + */ + public function unregisterPlugin($type, $name) { + if (isset($this->registered_plugins[$type][$name])) { + unset($this->registered_plugins[$type][$name]); + } + return $this; + } + + /** + * Adds directory of plugin files + * + * @param null|array|string $plugins_dir + * + * @return static current Smarty instance for chaining + * @deprecated since 5.0 + */ + public function addPluginsDir($plugins_dir) { + trigger_error('Using Smarty::addPluginsDir() to load plugins is deprecated and will be ' . + 'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::registerPlugin to ' . + 'quickly register a plugin using a callback function.', E_USER_DEPRECATED); + + foreach ((array)$plugins_dir as $v) { + $path = $this->_realpath(rtrim($v ?? '', '/\\') . DIRECTORY_SEPARATOR, true); + $this->BCPluginsAdapter->loadPluginsFromDir($path); + } + + return $this; + } + + /** + * Get plugin directories + * + * @return array list of plugin directories + * @deprecated since 5.0 + */ + public function getPluginsDir() { + trigger_error('Using Smarty::getPluginsDir() is deprecated and will be ' . + 'removed in a future release. It will always return an empty array.', E_USER_DEPRECATED); + return []; + } + + /** + * Set plugins directory + * + * @param string|array $plugins_dir directory(s) of plugins + * + * @return static current Smarty instance for chaining + * @deprecated since 5.0 + */ + public function setPluginsDir($plugins_dir) { + trigger_error('Using Smarty::getPluginsDir() is deprecated and will be ' . + 'removed in a future release. For now, it will remove the DefaultExtension from the extensions list and ' . + 'proceed to call Smartyy::addPluginsDir..', E_USER_DEPRECATED); + + $this->extensions = array_filter( + $this->extensions, + function ($extension) { + return !($extension instanceof DefaultExtension); + } + ); + + return $this->addPluginsDir($plugins_dir); + } + + /** + * Registers a default plugin handler + * + * @param callable $callback class/method name + * + * @return $this + * @throws Exception if $callback is not callable + * + * @api Smarty::registerDefaultPluginHandler() + * + * @deprecated since 5.0 + */ + public function registerDefaultPluginHandler($callback) { + + trigger_error('Using Smarty::registerDefaultPluginHandler() is deprecated and will be ' . + 'removed in a future release. Please rewrite your plugin handler as an extension.', + E_USER_DEPRECATED); + + if (is_callable($callback)) { + $this->default_plugin_handler_func = $callback; + } else { + throw new Exception("Default plugin handler '$callback' not callable"); + } + return $this; + } + + /** + * Get compiled directory + * + * @return string path to compiled templates + */ + public function getCompileDir() { + if (!$this->_compileDirNormalized) { + $this->_normalizeDir('compile_dir', $this->compile_dir); + $this->_compileDirNormalized = true; + } + return $this->compile_dir; + } + + /** + * + * @param string $compile_dir directory to store compiled templates in + * + * @return static current Smarty instance for chaining + */ + public function setCompileDir($compile_dir) { + $this->_normalizeDir('compile_dir', $compile_dir); + $this->_compileDirNormalized = true; + return $this; + } + + /** + * Get cache directory + * + * @return string path of cache directory + */ + public function getCacheDir() { + if (!$this->_cacheDirNormalized) { + $this->_normalizeDir('cache_dir', $this->cache_dir); + $this->_cacheDirNormalized = true; + } + return $this->cache_dir; + } + + /** + * Set cache directory + * + * @param string $cache_dir directory to store cached templates in + * + * @return static current Smarty instance for chaining + */ + public function setCacheDir($cache_dir) { + $this->_normalizeDir('cache_dir', $cache_dir); + $this->_cacheDirNormalized = true; + return $this; + } + + private $templates = []; + + /** + * Creates a template object + * + * @param string $template_name + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param null $parent next higher level of Smarty variables + * + * @return Template template object + * @throws Exception + */ + public function createTemplate($template_name, $cache_id = null, $compile_id = null, $parent = null): Template { + + $data = []; + + // Shuffle params for backward compatibility: if 2nd param is an object, it's the parent + if (is_object($cache_id)) { + $parent = $cache_id; + $cache_id = null; + } + + // Shuffle params for backward compatibility: if 2nd param is an array, it's data + if (is_array($cache_id)) { + $data = $cache_id; + $cache_id = null; + } + + return $this->doCreateTemplate($template_name, $cache_id, $compile_id, $parent, null, null, false, $data); + } + + /** + * Get unique template id + * + * @param string $resource_name + * @param null|mixed $cache_id + * @param null|mixed $compile_id + * @param null $caching + * + * @return string + */ + private function generateUniqueTemplateId( + $resource_name, + $cache_id = null, + $compile_id = null, + $caching = null + ): string { + // defaults for optional params + $cache_id = $cache_id ?? $this->cache_id; + $compile_id = $compile_id ?? $this->compile_id; + $caching = (int)$caching ?? $this->caching; + + // Add default resource type to resource name if it is missing + if (strpos($resource_name, ':') === false) { + $resource_name = "{$this->default_resource_type}:{$resource_name}"; + } + + $_templateId = $resource_name . '#' . $cache_id . '#' . $compile_id . '#' . $caching; + + // hash very long IDs to prevent problems with filename length + // do not hash shorter IDs, so they remain recognizable + if (strlen($_templateId) > 150) { + $_templateId = sha1($_templateId); + } + + return $_templateId; + } + + /** + * Normalize path + * - remove /./ and /../ + * - make it absolute if required + * + * @param string $path file path + * @param bool $realpath if true - convert to absolute + * false - convert to relative + * null - keep as it is but + * remove /./ /../ + * + * @return string + */ + public function _realpath($path, $realpath = null) { + $nds = ['/' => '\\', '\\' => '/']; + preg_match( + '%^(?(?:[[:alpha:]]:[\\\\/]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?(.*))$%u', + $path, + $parts + ); + $path = $parts['path']; + if ($parts['root'] === '\\') { + $parts['root'] = substr(getcwd(), 0, 2) . $parts['root']; + } else { + if ($realpath !== null && !$parts['root']) { + $path = getcwd() . DIRECTORY_SEPARATOR . $path; + } + } + // normalize DIRECTORY_SEPARATOR + $path = str_replace($nds[DIRECTORY_SEPARATOR], DIRECTORY_SEPARATOR, $path); + $parts['root'] = str_replace($nds[DIRECTORY_SEPARATOR], DIRECTORY_SEPARATOR, $parts['root']); + do { + $path = preg_replace( + ['#[\\\\/]{2}#', '#[\\\\/][.][\\\\/]#', '#[\\\\/]([^\\\\/.]+)[\\\\/][.][.][\\\\/]#'], + DIRECTORY_SEPARATOR, + $path, + -1, + $count + ); + } while ($count > 0); + return $realpath !== false ? $parts['root'] . $path : str_ireplace(getcwd(), '.', $parts['root'] . $path); + } + + /** + * @param boolean $use_sub_dirs + */ + public function setUseSubDirs($use_sub_dirs) { + $this->use_sub_dirs = $use_sub_dirs; + } + + /** + * @param int $error_reporting + */ + public function setErrorReporting($error_reporting) { + $this->error_reporting = $error_reporting; + } + + /** + * @param boolean $escape_html + */ + public function setEscapeHtml($escape_html) { + $this->escape_html = $escape_html; + } + + /** + * Return auto_literal flag + * + * @return boolean + */ + public function getAutoLiteral() { + return $this->auto_literal; + } + + /** + * Set auto_literal flag + * + * @param boolean $auto_literal + */ + public function setAutoLiteral($auto_literal = true) { + $this->auto_literal = $auto_literal; + } + + /** + * @param boolean $force_compile + */ + public function setForceCompile($force_compile) { + $this->force_compile = $force_compile; + } + + /** + * @param boolean $merge_compiled_includes + */ + public function setMergeCompiledIncludes($merge_compiled_includes) { + $this->merge_compiled_includes = $merge_compiled_includes; + } + + /** + * Get left delimiter + * + * @return string + */ + public function getLeftDelimiter() { + return $this->left_delimiter; + } + + /** + * Set left delimiter + * + * @param string $left_delimiter + */ + public function setLeftDelimiter($left_delimiter) { + $this->left_delimiter = $left_delimiter; + } + + /** + * Get right delimiter + * + * @return string $right_delimiter + */ + public function getRightDelimiter() { + return $this->right_delimiter; + } + + /** + * Set right delimiter + * + * @param string + */ + public function setRightDelimiter($right_delimiter) { + $this->right_delimiter = $right_delimiter; + } + + /** + * @param boolean $debugging + */ + public function setDebugging($debugging) { + $this->debugging = $debugging; + } + + /** + * @param boolean $config_overwrite + */ + public function setConfigOverwrite($config_overwrite) { + $this->config_overwrite = $config_overwrite; + } + + /** + * @param boolean $config_booleanize + */ + public function setConfigBooleanize($config_booleanize) { + $this->config_booleanize = $config_booleanize; + } + + /** + * @param boolean $config_read_hidden + */ + public function setConfigReadHidden($config_read_hidden) { + $this->config_read_hidden = $config_read_hidden; + } + + /** + * @param boolean $compile_locking + */ + public function setCompileLocking($compile_locking) { + $this->compile_locking = $compile_locking; + } + + /** + * @param string $default_resource_type + */ + public function setDefaultResourceType($default_resource_type) { + $this->default_resource_type = $default_resource_type; + } + + /** + * Test install + * + * @param null $errors + */ + public function testInstall(&$errors = null) { + \Smarty\TestInstall::testInstall($this, $errors); + } + + /** + * Get Smarty object + * + * @return static + */ + public function getSmarty() { + return $this; + } + + /** + * Normalize and set directory string + * + * @param string $dirName cache_dir or compile_dir + * @param string $dir filepath of folder + */ + private function _normalizeDir($dirName, $dir) { + $this->{$dirName} = $this->_realpath(rtrim($dir ?? '', "/\\") . DIRECTORY_SEPARATOR, true); + } + + /** + * Normalize template_dir or config_dir + * + * @param bool $isConfig true for config_dir + */ + private function _normalizeTemplateConfig($isConfig) { + if ($isConfig) { + $processed = &$this->_processedConfigDir; + $dir = &$this->config_dir; + } else { + $processed = &$this->_processedTemplateDir; + $dir = &$this->template_dir; + } + if (!is_array($dir)) { + $dir = (array)$dir; + } + foreach ($dir as $k => $v) { + if (!isset($processed[$k])) { + $dir[$k] = $this->_realpath(rtrim($v ?? '', "/\\") . DIRECTORY_SEPARATOR, true); + $processed[$k] = true; + } + } + + if ($isConfig) { + $this->_configDirNormalized = true; + $this->_joined_config_dir = join('#', $this->config_dir); + } else { + $this->_templateDirNormalized = true; + $this->_joined_template_dir = join('#', $this->template_dir); + } + + } + + /** + * Mutes errors for "undefined index", "undefined array key" and "trying to read property of null". + * + * @void + */ + public function muteUndefinedOrNullWarnings(): void { + $this->isMutingUndefinedOrNullWarnings = true; + } + + /** + * Indicates if Smarty will mute errors for "undefined index", "undefined array key" and "trying to read property of null". + * + * @return bool + */ + public function isMutingUndefinedOrNullWarnings(): bool { + return $this->isMutingUndefinedOrNullWarnings; + } + + /** + * Empty cache for a specific template + * + * @param string $template_name template name + * @param string $cache_id cache id + * @param string $compile_id compile id + * @param integer $exp_time expiration time + * @param string $type resource type + * + * @return int number of cache files deleted + * @throws \Smarty\Exception + * + * @api Smarty::clearCache() + */ + public function clearCache( + $template_name, + $cache_id = null, + $compile_id = null, + $exp_time = null + ) { + return $this->getCacheResource()->clear($this, $template_name, $cache_id, $compile_id, $exp_time); + } + + /** + * Empty cache folder + * + * @param integer $exp_time expiration time + * @param string $type resource type + * + * @return int number of cache files deleted + * + * @api Smarty::clearAllCache() + */ + public function clearAllCache($exp_time = null) { + return $this->getCacheResource()->clearAll($this, $exp_time); + } + + /** + * Delete compiled template file + * + * @param string $resource_name template name + * @param string $compile_id compile id + * @param integer $exp_time expiration time + * + * @return int number of template files deleted + * @throws \Smarty\Exception + * + * @api Smarty::clearCompiledTemplate() + */ + public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) { + $_compile_dir = $this->getCompileDir(); + if ($_compile_dir === '/') { //We should never want to delete this! + return 0; + } + $_compile_id = isset($compile_id) ? preg_replace('![^\w]+!', '_', $compile_id) : null; + $_dir_sep = $this->use_sub_dirs ? DIRECTORY_SEPARATOR : '^'; + if (isset($resource_name)) { + $_save_stat = $this->caching; + $this->caching = \Smarty\Smarty::CACHING_OFF; + /* @var Template $tpl */ + $tpl = $this->doCreateTemplate($resource_name); + $this->caching = $_save_stat; + if (!$tpl->getSource()->handler->recompiled && $tpl->getSource()->exists) { + $_resource_part_1 = basename(str_replace('^', DIRECTORY_SEPARATOR, $tpl->getCompiled()->filepath)); + $_resource_part_1_length = strlen($_resource_part_1); + } else { + return 0; + } + $_resource_part_2 = str_replace('.php', '.cache.php', $_resource_part_1); + $_resource_part_2_length = strlen($_resource_part_2); + } + $_dir = $_compile_dir; + if ($this->use_sub_dirs && isset($_compile_id)) { + $_dir .= $_compile_id . $_dir_sep; + } + if (isset($_compile_id)) { + $_compile_id_part = $_compile_dir . $_compile_id . $_dir_sep; + $_compile_id_part_length = strlen($_compile_id_part); + } + $_count = 0; + try { + $_compileDirs = new RecursiveDirectoryIterator($_dir); + } catch (\UnexpectedValueException $e) { + // path not found / not a dir + return 0; + } + $_compile = new RecursiveIteratorIterator($_compileDirs, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($_compile as $_file) { + if (substr(basename($_file->getPathname()), 0, 1) === '.') { + continue; + } + $_filepath = (string)$_file; + if ($_file->isDir()) { + if (!$_compile->isDot()) { + // delete folder if empty + @rmdir($_file->getPathname()); + } + } else { + // delete only php files + if (substr($_filepath, -4) !== '.php') { + continue; + } + $unlink = false; + if ((!isset($_compile_id) || + (isset($_filepath[$_compile_id_part_length]) && + $a = !strncmp($_filepath, $_compile_id_part, $_compile_id_part_length))) + && (!isset($resource_name) || (isset($_filepath[$_resource_part_1_length]) + && substr_compare( + $_filepath, + $_resource_part_1, + -$_resource_part_1_length, + $_resource_part_1_length + ) === 0) || (isset($_filepath[$_resource_part_2_length]) + && substr_compare( + $_filepath, + $_resource_part_2, + -$_resource_part_2_length, + $_resource_part_2_length + ) === 0)) + ) { + if (isset($exp_time)) { + if (is_file($_filepath) && time() - filemtime($_filepath) >= $exp_time) { + $unlink = true; + } + } else { + $unlink = true; + } + } + if ($unlink && is_file($_filepath) && @unlink($_filepath)) { + $_count++; + if (function_exists('opcache_invalidate') + && (!function_exists('ini_get') || strlen(ini_get('opcache.restrict_api')) < 1) + ) { + opcache_invalidate($_filepath, true); + } elseif (function_exists('apc_delete_file')) { + apc_delete_file($_filepath); + } + } + } + } + return $_count; + } + + /** + * Compile all template files + * + * @param string $extension file extension + * @param bool $force_compile force all to recompile + * @param int $time_limit + * @param int $max_errors + * + * @return integer number of template files recompiled + * @api Smarty::compileAllTemplates() + * + */ + public function compileAllTemplates( + $extension = '.tpl', + $force_compile = false, + $time_limit = 0, + $max_errors = null + ) { + return $this->compileAll($extension, $force_compile, $time_limit, $max_errors); + } + + /** + * Compile all config files + * + * @param string $extension file extension + * @param bool $force_compile force all to recompile + * @param int $time_limit + * @param int $max_errors + * + * @return int number of template files recompiled + * @api Smarty::compileAllConfig() + * + */ + public function compileAllConfig( + $extension = '.conf', + $force_compile = false, + $time_limit = 0, + $max_errors = null + ) { + return $this->compileAll($extension, $force_compile, $time_limit, $max_errors, true); + } + + /** + * Compile all template or config files + * + * @param string $extension template file name extension + * @param bool $force_compile force all to recompile + * @param int $time_limit set maximum execution time + * @param int $max_errors set maximum allowed errors + * @param bool $isConfig flag true if called for config files + * + * @return int number of template files compiled + */ + protected function compileAll( + $extension, + $force_compile, + $time_limit, + $max_errors, + $isConfig = false + ) { + // switch off time limit + if (function_exists('set_time_limit')) { + @set_time_limit($time_limit); + } + $_count = 0; + $_error_count = 0; + $sourceDir = $isConfig ? $this->getConfigDir() : $this->getTemplateDir(); + // loop over array of source directories + foreach ($sourceDir as $_dir) { + $_dir_1 = new RecursiveDirectoryIterator( + $_dir, + defined('FilesystemIterator::FOLLOW_SYMLINKS') ? + FilesystemIterator::FOLLOW_SYMLINKS : 0 + ); + $_dir_2 = new RecursiveIteratorIterator($_dir_1); + foreach ($_dir_2 as $_fileinfo) { + $_file = $_fileinfo->getFilename(); + if (substr(basename($_fileinfo->getPathname()), 0, 1) === '.' || strpos($_file, '.svn') !== false) { + continue; + } + if (substr_compare($_file, $extension, -strlen($extension)) !== 0) { + continue; + } + if ($_fileinfo->getPath() !== substr($_dir, 0, -1)) { + $_file = substr($_fileinfo->getPath(), strlen($_dir)) . DIRECTORY_SEPARATOR . $_file; + } + echo "\n", $_dir, '---', $_file; + flush(); + $_start_time = microtime(true); + $_smarty = clone $this; + // + $_smarty->force_compile = $force_compile; + try { + $_tpl = $this->doCreateTemplate($_file); + $_tpl->caching = self::CACHING_OFF; + $_tpl->setSource( + $isConfig ? \Smarty\Template\Config::load($_tpl) : \Smarty\Template\Source::load($_tpl) + ); + if ($_tpl->mustCompile()) { + $_tpl->compileTemplateSource(); + $_count++; + echo ' compiled in ', microtime(true) - $_start_time, ' seconds'; + flush(); + } else { + echo ' is up to date'; + flush(); + } + } catch (\Exception $e) { + echo "\n ------>Error: ", $e->getMessage(), "\n"; + $_error_count++; + } + // free memory + unset($_tpl); + if ($max_errors !== null && $_error_count === $max_errors) { + echo "\ntoo many errors\n"; + exit(1); + } + } + } + echo "\n"; + return $_count; + } + + /** + * check client side cache + * + * @param \Smarty\Template\Cached $cached + * @param Template $_template + * @param string $content + * + * @throws \Exception + * @throws \Smarty\Exception + */ + public function cacheModifiedCheck(Template\Cached $cached, Template $_template, $content) { + $_isCached = $_template->isCached() && !$_template->getCompiled()->getNocacheCode(); + $_last_modified_date = + @substr($_SERVER['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3); + if ($_isCached && $cached->timestamp <= strtotime($_last_modified_date)) { + switch (PHP_SAPI) { + case 'cgi': // php-cgi < 5.3 + case 'cgi-fcgi': // php-cgi >= 5.3 + case 'fpm-fcgi': // php-fpm >= 5.3.3 + header('Status: 304 Not Modified'); + break; + case 'cli': + if (/* ^phpunit */ + !empty($_SERVER['SMARTY_PHPUNIT_DISABLE_HEADERS']) /* phpunit$ */ + ) { + $_SERVER['SMARTY_PHPUNIT_HEADERS'][] = '304 Not Modified'; + } + break; + default: + if (/* ^phpunit */ + !empty($_SERVER['SMARTY_PHPUNIT_DISABLE_HEADERS']) /* phpunit$ */ + ) { + $_SERVER['SMARTY_PHPUNIT_HEADERS'][] = '304 Not Modified'; + } else { + header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified'); + } + break; + } + } else { + switch (PHP_SAPI) { + case 'cli': + if (/* ^phpunit */ + !empty($_SERVER['SMARTY_PHPUNIT_DISABLE_HEADERS']) /* phpunit$ */ + ) { + $_SERVER['SMARTY_PHPUNIT_HEADERS'][] = + 'Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT'; + } + break; + default: + header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT'); + break; + } + echo $content; + } + } + + public function getModifierCallback(string $modifierName) { + foreach ($this->getExtensions() as $extension) { + if ($callback = $extension->getModifierCallback($modifierName)) { + return [new CallbackWrapper($modifierName, $callback), 'handle']; + } + } + return null; + } + + public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface { + foreach ($this->getExtensions() as $extension) { + if ($handler = $extension->getFunctionHandler($functionName)) { + return $handler; + } + } + return null; + } + + public function getBlockHandler(string $blockTagName): ?\Smarty\BlockHandler\BlockHandlerInterface { + foreach ($this->getExtensions() as $extension) { + if ($handler = $extension->getBlockHandler($blockTagName)) { + return $handler; + } + } + return null; + } + + public function getModifierCompiler(string $modifier): ?\Smarty\Compile\Modifier\ModifierCompilerInterface { + foreach ($this->getExtensions() as $extension) { + if ($handler = $extension->getModifierCompiler($modifier)) { + return $handler; + } + } + return null; + } + + /** + * Run pre-filters over template source + * + * @param string $source the content which shall be processed by the filters + * @param Template $template template object + * + * @return string the filtered source + */ + public function runPreFilters($source, Template $template) { + + foreach ($this->getExtensions() as $extension) { + /** @var \Smarty\Filter\FilterInterface $filter */ + foreach ($extension->getPreFilters() as $filter) { + $source = $filter->filter($source, $template); + } + } + + // return filtered output + return $source; + } + + /** + * Run post-filters over template's compiled code + * + * @param string $code the content which shall be processed by the filters + * @param Template $template template object + * + * @return string the filtered code + */ + public function runPostFilters($code, Template $template) { + + foreach ($this->getExtensions() as $extension) { + /** @var \Smarty\Filter\FilterInterface $filter */ + foreach ($extension->getPostFilters() as $filter) { + $code = $filter->filter($code, $template); + } + } + + // return filtered output + return $code; + } + + /** + * Run filters over template output + * + * @param string $content the content which shall be processed by the filters + * @param Template $template template object + * + * @return string the filtered (modified) output + */ + public function runOutputFilters($content, Template $template) { + + foreach ($this->getExtensions() as $extension) { + /** @var \Smarty\Filter\FilterInterface $filter */ + foreach ($extension->getOutputFilters() as $filter) { + $content = $filter->filter($content, $template); + } + } + + // return filtered output + return $content; + } + + /** + * Writes file in a safe way to disk + * + * @param string $_filepath complete filepath + * @param string $_contents file content + * + * @return boolean true + * @throws Exception + */ + public function writeFile($_filepath, $_contents) { + $_error_reporting = error_reporting(); + error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING); + $_dirpath = dirname($_filepath); + // if subdirs, create dir structure + if ($_dirpath !== '.') { + $i = 0; + // loop if concurrency problem occurs + // see https://bugs.php.net/bug.php?id=35326 + while (!is_dir($_dirpath)) { + if (@mkdir($_dirpath, 0777, true)) { + break; + } + clearstatcache(); + if (++$i === 3) { + error_reporting($_error_reporting); + throw new Exception("unable to create directory {$_dirpath}"); + } + sleep(1); + } + } + // write to tmp file, then move to overt file lock race condition + $_tmp_file = $_dirpath . DIRECTORY_SEPARATOR . str_replace(['.', ','], '_', uniqid('wrt', true)); + if (!file_put_contents($_tmp_file, $_contents)) { + error_reporting($_error_reporting); + throw new Exception("unable to write file {$_tmp_file}"); + } + /* + * Windows' rename() fails if the destination exists, + * Linux' rename() properly handles the overwrite. + * Simply unlink()ing a file might cause other processes + * currently reading that file to fail, but linux' rename() + * seems to be smart enough to handle that for us. + */ + if (\Smarty\Smarty::$_IS_WINDOWS) { + // remove original file + if (is_file($_filepath)) { + @unlink($_filepath); + } + // rename tmp file + $success = @rename($_tmp_file, $_filepath); + } else { + // rename tmp file + $success = @rename($_tmp_file, $_filepath); + if (!$success) { + // remove original file + if (is_file($_filepath)) { + @unlink($_filepath); + } + // rename tmp file + $success = @rename($_tmp_file, $_filepath); + } + } + if (!$success) { + error_reporting($_error_reporting); + throw new Exception("unable to write file {$_filepath}"); + } + // set file permissions + @chmod($_filepath, 0666 & ~umask()); + error_reporting($_error_reporting); + return true; + } + + private $runtimes = []; + + /** + * Loads and returns a runtime extension or null if not found + * + * @param string $type + * + * @return object|null + */ + public function getRuntime(string $type) { + + if (isset($this->runtimes[$type])) { + return $this->runtimes[$type]; + } + + // Lazy load runtimes when/if needed + switch ($type) { + case 'Capture': + return $this->runtimes[$type] = new CaptureRuntime(); + case 'Foreach': + return $this->runtimes[$type] = new ForeachRuntime(); + case 'Inheritance': + return $this->runtimes[$type] = new InheritanceRuntime(); + case 'TplFunction': + return $this->runtimes[$type] = new TplFunctionRuntime(); + case 'DefaultPluginHandler': + return $this->runtimes[$type] = new DefaultPluginHandlerRuntime( + $this->getDefaultPluginHandlerFunc() + ); + } + + throw new \Smarty\Exception('Trying to load invalid runtime ' . $type); + } + + /** + * Indicates if a runtime is available. + * + * @param string $type + * + * @return bool + */ + public function hasRuntime(string $type): bool { + try { + $this->getRuntime($type); + return true; + } catch (\Smarty\Exception $e) { + return false; + } + } + + /** + * @return callable|null + */ + public function getDefaultPluginHandlerFunc(): ?callable { + return $this->default_plugin_handler_func; + } + + /** + * load a filter of specified type and name + * + * @param string $type filter type + * @param string $name filter name + * + * @return bool + * @throws \Smarty\Exception + * @api Smarty::loadFilter() + * + * @deprecated since 5.0 + */ + public function loadFilter($type, $name) { + + if ($type == \Smarty\Smarty::FILTER_VARIABLE) { + foreach ($this->getExtensions() as $extension) { + if ($extension->getModifierCallback($name)) { + + trigger_error('Using Smarty::loadFilter() to load variable filters is deprecated and will ' . + 'be removed in a future release. Use Smarty::addDefaultModifiers() to add a modifier.', + E_USER_DEPRECATED); + + $this->addDefaultModifiers([$name]); + return true; + } + } + } + + trigger_error('Using Smarty::loadFilter() to load filters is deprecated and will be ' . + 'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::registerFilter to ' . + 'quickly register a filter using a callback function.', E_USER_DEPRECATED); + + if ($type == \Smarty\Smarty::FILTER_OUTPUT && $name == 'trimwhitespace') { + $this->BCPluginsAdapter->addOutputFilter(new TrimWhitespace()); + return true; + } + + $_plugin = "smarty_{$type}filter_{$name}"; + if (!is_callable($_plugin) && class_exists($_plugin, false)) { + $_plugin = [$_plugin, 'execute']; + } + + if (is_callable($_plugin)) { + $this->registerFilter($type, $_plugin, $name); + return true; + } + + throw new Exception("{$type}filter '{$name}' not found or callable"); + } + + /** + * load a filter of specified type and name + * + * @param string $type filter type + * @param string $name filter name + * + * @return static + * @throws \Smarty\Exception + * @api Smarty::unloadFilter() + * + * + * @deprecated since 5.0 + */ + public function unloadFilter($type, $name) { + trigger_error('Using Smarty::unloadFilter() to unload filters is deprecated and will be ' . + 'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::(un)registerFilter to ' . + 'quickly (un)register a filter using a callback function.', E_USER_DEPRECATED); + + return $this->unregisterFilter($type, $name); + } + + private $_caching_type = 'file'; + + /** + * @param $type + * + * @return void + * @deprecated since 5.0 + */ + public function setCachingType($type) { + trigger_error('Using Smarty::setCachingType() is deprecated and will be ' . + 'removed in a future release. Use Smarty::setCacheResource() instead.', E_USER_DEPRECATED); + $this->_caching_type = $type; + $this->activateBCCacheResource(); + } + + /** + * @return string + * @deprecated since 5.0 + */ + public function getCachingType(): string { + trigger_error('Using Smarty::getCachingType() is deprecated and will be ' . + 'removed in a future release.', E_USER_DEPRECATED); + return $this->_caching_type; + } + + /** + * Registers a resource to fetch a template + * + * @param string $name name of resource type + * @param Base $resource_handler + * + * @return static + * + * @api Smarty::registerCacheResource() + * + * @deprecated since 5.0 + */ + public function registerCacheResource($name, \Smarty\Cacheresource\Base $resource_handler) { + + trigger_error('Using Smarty::registerCacheResource() is deprecated and will be ' . + 'removed in a future release. Use Smarty::setCacheResource() instead.', E_USER_DEPRECATED); + + $this->registered_cache_resources[$name] = $resource_handler; + $this->activateBCCacheResource(); + return $this; + } + + /** + * Unregisters a resource to fetch a template + * + * @param $name + * + * @return static + * @api Smarty::unregisterCacheResource() + * + * @deprecated since 5.0 + * + */ + public function unregisterCacheResource($name) { + + trigger_error('Using Smarty::unregisterCacheResource() is deprecated and will be ' . + 'removed in a future release.', E_USER_DEPRECATED); + + if (isset($this->registered_cache_resources[$name])) { + unset($this->registered_cache_resources[$name]); + } + return $this; + } + + private function activateBCCacheResource() { + if ($this->_caching_type == 'file') { + $this->setCacheResource(new File()); + } + if (isset($this->registered_cache_resources[$this->_caching_type])) { + $this->setCacheResource($this->registered_cache_resources[$this->_caching_type]); + } + } + + /** + * Registers a filter function + * + * @param string $type filter type + * @param callable $callback + * @param string|null $name optional filter name + * + * @return static + * @throws \Smarty\Exception + * + * @api Smarty::registerFilter() + */ + public function registerFilter($type, $callback, $name = null) { + $name = $name ?? $this->_getFilterName($callback); + if (!is_callable($callback)) { + throw new Exception("{$type}filter '{$name}' not callable"); + } + switch ($type) { + case 'variable': + $this->registerPlugin(self::PLUGIN_MODIFIER, $name, $callback); + trigger_error('Using Smarty::registerFilter() to register variable filters is deprecated and ' . + 'will be removed in a future release. Use Smarty::addDefaultModifiers() to add a modifier.', + E_USER_DEPRECATED); + + $this->addDefaultModifiers([$name]); + break; + case 'output': + $this->BCPluginsAdapter->addCallableAsOutputFilter($callback, $name); + break; + case 'pre': + $this->BCPluginsAdapter->addCallableAsPreFilter($callback, $name); + break; + case 'post': + $this->BCPluginsAdapter->addCallableAsPostFilter($callback, $name); + break; + default: + throw new Exception("Illegal filter type '{$type}'"); + } + + return $this; + } + + /** + * Return internal filter name + * + * @param callback $callable + * + * @return string|null internal filter name or null if callable cannot be serialized + */ + private function _getFilterName($callable) { + if (is_array($callable)) { + $_class_name = is_object($callable[0]) ? get_class($callable[0]) : $callable[0]; + return $_class_name . '_' . $callable[1]; + } elseif (is_string($callable)) { + return $callable; + } + return null; + } + + /** + * Unregisters a filter function. Smarty cannot unregister closures/anonymous functions if + * no name was given in ::registerFilter. + * + * @param string $type filter type + * @param callback|string $name the name previously used in ::registerFilter + * + * @return static + * @throws \Smarty\Exception + * @api Smarty::unregisterFilter() + * + * + */ + public function unregisterFilter($type, $name) { + + if (!is_string($name)) { + $name = $this->_getFilterName($name); + } + + if ($name) { + switch ($type) { + case 'output': + $this->BCPluginsAdapter->removeOutputFilter($name); + break; + case 'pre': + $this->BCPluginsAdapter->removePreFilter($name); + break; + case 'post': + $this->BCPluginsAdapter->removePostFilter($name); + break; + default: + throw new Exception("Illegal filter type '{$type}'"); + } + } + + return $this; + } + + /** + * Add default modifiers + * + * @param array|string $modifiers modifier or list of modifiers + * to add + * + * @return static + * @api Smarty::addDefaultModifiers() + * + */ + public function addDefaultModifiers($modifiers) { + if (is_array($modifiers)) { + $this->default_modifiers = array_merge($this->default_modifiers, $modifiers); + } else { + $this->default_modifiers[] = $modifiers; + } + return $this; + } + + /** + * Get default modifiers + * + * @return array list of default modifiers + * @api Smarty::getDefaultModifiers() + * + */ + public function getDefaultModifiers() { + return $this->default_modifiers; + } + + /** + * Set default modifiers + * + * @param array|string $modifiers modifier or list of modifiers + * to set + * + * @return static + * @api Smarty::setDefaultModifiers() + * + */ + public function setDefaultModifiers($modifiers) { + $this->default_modifiers = (array)$modifiers; + return $this; + } + + /** + * @return Cacheresource\Base + */ + public function getCacheResource(): Cacheresource\Base { + return $this->cacheResource; + } + + /** + * @param Cacheresource\Base $cacheResource + */ + public function setCacheResource(Cacheresource\Base $cacheResource): void { + $this->cacheResource = $cacheResource; + } + + /** + * fetches a rendered Smarty template + * + * @param string $template the resource handle of the template file or template object + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * + * @return string rendered template output + * @throws Exception + * @throws Exception + */ + public function fetch($template = null, $cache_id = null, $compile_id = null) { + return $this->returnOrCreateTemplate($template, $cache_id, $compile_id)->fetch(); + } + + /** + * displays a Smarty template + * + * @param string $template the resource handle of the template file or template object + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * + * @throws \Exception + * @throws \Smarty\Exception + */ + public function display($template = null, $cache_id = null, $compile_id = null) { + $this->returnOrCreateTemplate($template, $cache_id, $compile_id)->display(); + } + + /** + * @param $resource_name + * @param $cache_id + * @param $compile_id + * @param $parent + * @param $caching + * @param $cache_lifetime + * @param bool $isConfig + * @param array $data + * + * @return Template + * @throws Exception + */ + public function doCreateTemplate( + $resource_name, + $cache_id = null, + $compile_id = null, + $parent = null, + $caching = null, + $cache_lifetime = null, + bool $isConfig = false, + array $data = []): Template { + + if (!$this->_templateDirNormalized) { + $this->_normalizeTemplateConfig(false); + } + + $_templateId = $this->generateUniqueTemplateId($resource_name, $cache_id, $compile_id, $caching); + + if (!isset($this->templates[$_templateId])) { + $newTemplate = new Template($resource_name, $this, $parent ?: $this, $cache_id, $compile_id, $caching, $isConfig); + $newTemplate->templateId = $_templateId; // @TODO this could go in constructor ^? + $this->templates[$_templateId] = $newTemplate; + } + + $tpl = clone $this->templates[$_templateId]; + + $tpl->setParent($parent ?: $this); + + if ($cache_lifetime) { + $tpl->setCacheLifetime($cache_lifetime); + } + + // fill data if present + foreach ($data as $_key => $_val) { + $tpl->assign($_key, $_val); + } + + $tpl->tplFunctions = array_merge($parent->tplFunctions ?? [], $tpl->tplFunctions ?? []); + + if (!$this->debugging && $this->debugging_ctrl === 'URL') { + $tpl->getSmarty()->getDebug()->debugUrl($tpl->getSmarty()); + } + return $tpl; + } + + /** + * test if cache is valid + * + * @param null|string|Template $template the resource handle of the template file or template + * object + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * + * @return bool cache status + * @throws \Exception + * @throws \Smarty\Exception + * + * @api Smarty::isCached() + */ + public function isCached($template = null, $cache_id = null, $compile_id = null) { + return $this->returnOrCreateTemplate($template, $cache_id, $compile_id)->isCached(); + } + + /** + * @param $template + * @param $cache_id + * @param $compile_id + * @param $parent + * + * @return Template + * @throws Exception + */ + private function returnOrCreateTemplate($template, $cache_id = null, $compile_id = null) { + if (!($template instanceof Template)) { + $template = $this->createTemplate($template, $cache_id, $compile_id, $this); + $template->caching = $this->caching; + } + return $template; + } + + /** + * Sets if Smarty should check If-Modified-Since headers to determine cache validity. + * @param bool $cache_modified_check + * @return void + */ + public function setCacheModifiedCheck($cache_modified_check): void { + $this->cache_modified_check = (bool) $cache_modified_check; + } + +} + diff --git a/src/Template.php b/src/Template.php new file mode 100644 index 0000000..fcb0f58 --- /dev/null +++ b/src/Template.php @@ -0,0 +1,732 @@ +getSmarty()-getLeftDelimiter(). + * + * @var string + */ + private $left_delimiter = null; + + /** + * Template right-delimiter. If null, defaults to $this->getSmarty()-getRightDelimiter(). + * + * @var string + */ + private $right_delimiter = null; + + /** + * @var InheritanceRuntime|null + */ + private $inheritance; + + /** + * Create template data object + * Some of the global Smarty settings copied to template scope + * It load the required template resources and caching plugins + * + * @param string $template_resource template resource string + * @param Smarty $smarty Smarty instance + * @param \Smarty\Data|null $_parent back pointer to parent object with variables or null + * @param mixed $_cache_id cache id or null + * @param mixed $_compile_id compile id or null + * @param bool|int|null $_caching use caching? + * @param bool $_isConfig + * + * @throws \Smarty\Exception + */ + public function __construct( + $template_resource, + Smarty $smarty, + \Smarty\Data $_parent = null, + $_cache_id = null, + $_compile_id = null, + $_caching = null, + $_isConfig = false + ) { + $this->smarty = $smarty; + // Smarty parameter + $this->cache_id = $_cache_id === null ? $this->smarty->cache_id : $_cache_id; + $this->compile_id = $_compile_id === null ? $this->smarty->compile_id : $_compile_id; + $this->caching = (int)($_caching === null ? $this->smarty->caching : $_caching); + $this->cache_lifetime = $this->smarty->cache_lifetime; + $this->compile_check = (int)$smarty->compile_check; + $this->parent = $_parent; + // Template resource + $this->template_resource = $template_resource; + + $this->source = $_isConfig ? Config::load($this) : Source::load($this); + $this->compiled = Compiled::load($this); + + if ($smarty->security_policy) { + $smarty->security_policy->registerCallBacks($this); + } + } + + /** + * render template + * + * @param bool $no_output_filter if true do not run output filter + * @param null|bool $display true: display, false: fetch null: sub-template + * + * @return string + * @throws \Exception + * @throws \Smarty\Exception + */ + private function render($no_output_filter = true, $display = null) { + if ($this->smarty->debugging) { + $this->smarty->getDebug()->start_template($this, $display); + } + // checks if template exists + if ($this->compile_check && !$this->getSource()->exists) { + throw new Exception( + "Unable to load '{$this->getSource()->type}:{$this->getSource()->name}'" . + ($this->_isSubTpl() ? " in '{$this->parent->template_resource}'" : '') + ); + } + + // disable caching for evaluated code + if ($this->getSource()->handler->recompiled) { + $this->caching = \Smarty\Smarty::CACHING_OFF; + } + + foreach ($this->startRenderCallbacks as $callback) { + call_user_func($callback, $this); + } + + try { + + // read from cache or render + if ($this->caching === \Smarty\Smarty::CACHING_LIFETIME_CURRENT || $this->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED) { + $this->getCached()->render($this, $no_output_filter); + } else { + $this->getCompiled()->render($this); + } + + } finally { + foreach ($this->endRenderCallbacks as $callback) { + call_user_func($callback, $this); + } + } + + // display or fetch + if ($display) { + if ($this->caching && $this->smarty->cache_modified_check) { + $this->smarty->cacheModifiedCheck( + $this->getCached(), + $this, + isset($content) ? $content : ob_get_clean() + ); + } else { + if ((!$this->caching || $this->getCached()->getNocacheCode() || $this->getSource()->handler->recompiled) + && !$no_output_filter + ) { + echo $this->smarty->runOutputFilters(ob_get_clean(), $this); + } else { + echo ob_get_clean(); + } + } + if ($this->smarty->debugging) { + $this->smarty->getDebug()->end_template($this); + // debug output + $this->smarty->getDebug()->display_debug($this, true); + } + return ''; + } else { + if ($this->smarty->debugging) { + $this->smarty->getDebug()->end_template($this); + if ($this->smarty->debugging === 2 && $display === false) { + $this->smarty->getDebug()->display_debug($this, true); + } + } + if ( + !$no_output_filter + && (!$this->caching || $this->getCached()->getNocacheCode() || $this->getSource()->handler->recompiled) + ) { + + return $this->smarty->runOutputFilters(ob_get_clean(), $this); + } + // return cache content + return null; + } + } + + /** + * Runtime function to render sub-template + * + * @param string $template_name template name + * @param mixed $cache_id cache id + * @param mixed $compile_id compile id + * @param integer $caching cache mode + * @param integer $cache_lifetime lifetime of cache data + * @param array $extra_vars passed parameter template variables + * @param int|null $scope + * + * @throws Exception + */ + public function renderSubTemplate( + $template_name, + $cache_id, + $compile_id, + $caching, + $cache_lifetime, + array $extra_vars = [], + int $scope = null, + ?string $currentDir = null + ) { + + $name = $this->parseResourceName($template_name); + if ($currentDir && preg_match('/^\.{1,2}\//', $name)) { + // relative template resource name, append it to current template name + $template_name = $currentDir . DIRECTORY_SEPARATOR . $name; + } + + $tpl = $this->smarty->doCreateTemplate($template_name, $cache_id, $compile_id, $this, $caching, $cache_lifetime); + + $tpl->inheritance = $this->getInheritance(); // re-use the same Inheritance object inside the inheritance tree + + if ($scope) { + $tpl->defaultScope = $scope; + } + + if ($caching) { + if ($tpl->templateId !== $this->templateId && $caching !== \Smarty\Template::CACHING_NOCACHE_CODE) { + $tpl->getCached(true); + } else { + // re-use the same Cache object across subtemplates to gather hashes and file dependencies. + $tpl->setCached($this->getCached()); + } + } + + foreach ($extra_vars as $_key => $_val) { + $tpl->assign($_key, $_val); + } + if ($tpl->caching === \Smarty\Template::CACHING_NOCACHE_CODE) { + if ($tpl->getCompiled()->getNocacheCode()) { + $this->getCached()->hashes[$tpl->getCompiled()->nocache_hash] = true; + } + } + + $tpl->render(); + } + + /** + * Remove type indicator from resource name if present. + * E.g. $this->parseResourceName('file:template.tpl') returns 'template.tpl' + * + * @note "C:/foo.tpl" was forced to file resource up till Smarty 3.1.3 (including). + * + * @param string $resource_name template_resource or config_resource to parse + * + * @return string + */ + private function parseResourceName($resource_name): string { + if (preg_match('/^([A-Za-z0-9_\-]{2,}):/', $resource_name, $match)) { + return substr($resource_name, strlen($match[0])); + } + return $resource_name; + } + + /** + * Check if this is a sub template + * + * @return bool true is sub template + */ + public function _isSubTpl() { + return isset($this->parent) && $this->parent instanceof Template; + } + + public function assign($tpl_var, $value = null, $nocache = false, $scope = null) { + return parent::assign($tpl_var, $value, $nocache, $scope); + } + + /** + * Compiles the template + * If the template is not evaluated the compiled template is saved on disk + * + * @TODO only used in compileAll and 1 unit test: can we move this and make compileAndWrite private? + * + * @throws \Exception + */ + public function compileTemplateSource() { + return $this->getCompiled()->compileAndWrite($this); + } + + /** + * Return cached content + * + * @return null|string + * @throws Exception + */ + public function getCachedContent() { + return $this->getCached()->getContent($this); + } + + /** + * Writes the content to cache resource + * + * @param string $content + * + * @return bool + * + * @TODO this method is only used in unit tests that (mostly) try to test CacheResources. + */ + public function writeCachedContent($content) { + if ($this->getSource()->handler->recompiled || !$this->caching + ) { + // don't write cache file + return false; + } + $codeframe = $this->createCodeFrame($content, '', true); + return $this->getCached()->writeCache($this, $codeframe); + } + + /** + * Get unique template id + * + * @return string + */ + public function getTemplateId() { + return $this->templateId; + } + + /** + * runtime error not matching capture tags + * + * @throws \Smarty\Exception + */ + public function capture_error() { + throw new Exception("Not matching {capture} open/close in '{$this->template_resource}'"); + } + + /** + * Return Compiled object + * + * @param bool $forceNew force new compiled object + */ + public function getCompiled($forceNew = false) { + if ($forceNew || !isset($this->compiled)) { + $this->compiled = Compiled::load($this); + } + return $this->compiled; + } + + /** + * Return Cached object + * + * @param bool $forceNew force new cached object + * + * @throws Exception + */ + public function getCached($forceNew = false): Cached { + if ($forceNew || !isset($this->cached)) { + $cacheResource = $this->smarty->getCacheResource(); + $this->cached = new Cached( + $this->source, + $cacheResource, + $this->compile_id, + $this->cache_id + ); + if ($this->isCachingEnabled()) { + $cacheResource->populate($this->cached, $this); + } else { + $this->cached->setValid(false); + } + } + return $this->cached; + } + + private function isCachingEnabled(): bool { + return $this->caching && !$this->getSource()->handler->recompiled; + } + + /** + * Helper function for InheritanceRuntime object + * + * @return InheritanceRuntime + * @throws Exception + */ + public function getInheritance(): InheritanceRuntime { + if (is_null($this->inheritance)) { + $this->inheritance = clone $this->getSmarty()->getRuntime('Inheritance'); + } + return $this->inheritance; + } + + /** + * Sets a new InheritanceRuntime object. + * + * @param InheritanceRuntime $inheritanceRuntime + * + * @return void + */ + public function setInheritance(InheritanceRuntime $inheritanceRuntime) { + $this->inheritance = $inheritanceRuntime; + } + + /** + * Return Compiler object + */ + public function getCompiler() { + if (!isset($this->compiler)) { + $this->compiler = $this->getSource()->createCompiler(); + } + return $this->compiler; + } + + /** + * Create code frame for compiled and cached templates + * + * @param string $content optional template content + * @param string $functions compiled template function and block code + * @param bool $cache flag for cache file + * @param Compiler\Template|null $compiler + * + * @return string + * @throws Exception + */ + public function createCodeFrame($content = '', $functions = '', $cache = false, \Smarty\Compiler\Template $compiler = null) { + return $this->getCodeFrameCompiler()->create($content, $functions, $cache, $compiler); + } + + /** + * Template data object destructor + */ + public function __destruct() { + if ($this->smarty->cache_locking && $this->getCached()->is_locked) { + $this->getCached()->handler->releaseLock($this->smarty, $this->getCached()); + } + } + + /** + * Returns if the current template must be compiled by the Smarty compiler + * It does compare the timestamps of template source and the compiled templates and checks the force compile + * configuration + * + * @return bool + * @throws \Smarty\Exception + */ + public function mustCompile(): bool { + if (!$this->getSource()->exists) { + if ($this->_isSubTpl()) { + $parent_resource = " in '{$this->parent->template_resource}'"; + } else { + $parent_resource = ''; + } + throw new Exception("Unable to load {$this->getSource()->type} '{$this->getSource()->name}'{$parent_resource}"); + } + + // @TODO move this logic to Compiled + return $this->smarty->force_compile + || $this->getSource()->handler->recompiled + || !$this->getCompiled()->exists + || ($this->compile_check && $this->getCompiled()->getTimeStamp() < $this->getSource()->getTimeStamp()); + } + + private function getCodeFrameCompiler(): Compiler\CodeFrame { + return new \Smarty\Compiler\CodeFrame($this); + } + + /** + * Get left delimiter + * + * @return string + */ + public function getLeftDelimiter() + { + return $this->left_delimiter ?? $this->getSmarty()->getLeftDelimiter(); + } + + /** + * Set left delimiter + * + * @param string $left_delimiter + */ + public function setLeftDelimiter($left_delimiter) + { + $this->left_delimiter = $left_delimiter; + } + + /** + * Get right delimiter + * + * @return string $right_delimiter + */ + public function getRightDelimiter() + { + return $this->right_delimiter ?? $this->getSmarty()->getRightDelimiter();; + } + + /** + * Set right delimiter + * + * @param string + */ + public function setRightDelimiter($right_delimiter) + { + $this->right_delimiter = $right_delimiter; + } + + /** + * gets a stream variable + * + * @param string $variable the stream of the variable + * + * @return mixed + * @throws \Smarty\Exception + * + */ + public function getStreamVariable($variable) + { + + trigger_error("Using stream variables (\`\{\$foo:bar\}\`)is deprecated.", E_USER_DEPRECATED); + + $_result = ''; + $fp = fopen($variable, 'r+'); + if ($fp) { + while (!feof($fp) && ($current_line = fgets($fp)) !== false) { + $_result .= $current_line; + } + fclose($fp); + return $_result; + } + if ($this->getSmarty()->error_unassigned) { + throw new Exception('Undefined stream variable "' . $variable . '"'); + } + return null; + } + /** + * @inheritdoc + */ + public function configLoad($config_file, $sections = null) + { + $confObj = parent::configLoad($config_file, $sections); + + $this->getCompiled()->file_dependency[ $confObj->getSource()->uid ] = + array($confObj->getSource()->getResourceName(), $confObj->getSource()->getTimeStamp(), $confObj->getSource()->type); + + return $confObj; + } + + public function fetch() { + $result = $this->_execute(0); + return $result === null ? ob_get_clean() : $result; + } + + public function display() { + $this->_execute(1); + } + + /** + * test if cache is valid + * + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param object $parent next higher level of Smarty variables + * + * @return bool cache status + * @throws \Exception + * @throws \Smarty\Exception + * + * @api Smarty::isCached() + */ + public function isCached(): bool { + return (bool) $this->_execute(2); + } + + /** + * fetches a rendered Smarty template + * + * @param string $function function type 0 = fetch, 1 = display, 2 = isCache + * + * @return mixed + * @throws Exception + * @throws \Throwable + */ + private function _execute($function) { + + $smarty = $this->getSmarty(); + + // make sure we have integer values + $this->caching = (int)$this->caching; + // fetch template content + $level = ob_get_level(); + try { + $_smarty_old_error_level = + isset($smarty->error_reporting) ? error_reporting($smarty->error_reporting) : null; + + if ($smarty->isMutingUndefinedOrNullWarnings()) { + $errorHandler = new \Smarty\ErrorHandler(); + $errorHandler->activate(); + } + + if ($function === 2) { + if ($this->caching) { + // return cache status of template + $result = $this->getCached()->isCached($this); + } else { + return false; + } + } else { + + // After rendering a template, the tpl/config variables are reset, so the template can be re-used. + $this->pushStack(); + + // Start output-buffering. + ob_start(); + + $result = $this->render(false, $function); + + // Restore the template to its previous state + $this->popStack(); + } + + if (isset($errorHandler)) { + $errorHandler->deactivate(); + } + + if (isset($_smarty_old_error_level)) { + error_reporting($_smarty_old_error_level); + } + return $result; + } catch (\Throwable $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + if (isset($errorHandler)) { + $errorHandler->deactivate(); + } + + if (isset($_smarty_old_error_level)) { + error_reporting($_smarty_old_error_level); + } + throw $e; + } + } + + /** + * @return Config|Source|null + */ + public function getSource() { + return $this->source; + } + + /** + * @param Config|Source|null $source + */ + public function setSource($source): void { + $this->source = $source; + } + + /** + * Sets the Cached object, so subtemplates can share one Cached object to gather meta-data. + * + * @param Cached $cached + * + * @return void + */ + private function setCached(Cached $cached) { + $this->cached = $cached; + } + + /** + * @param string $compile_id + * + * @throws Exception + */ + public function setCompileId($compile_id) { + parent::setCompileId($compile_id); + $this->getCompiled(true); + if ($this->caching) { + $this->getCached(true); + } + } + + /** + * @param string $cache_id + * + * @throws Exception + */ + public function setCacheId($cache_id) { + parent::setCacheId($cache_id); + $this->getCached(true); + } + +} diff --git a/src/Template/Cached.php b/src/Template/Cached.php new file mode 100644 index 0000000..78635db --- /dev/null +++ b/src/Template/Cached.php @@ -0,0 +1,428 @@ +valid = $valid; + } + + /** + * CacheResource Handler + * + * @var \Smarty\Cacheresource\Base + */ + public $handler = null; + + /** + * Template Cache Id (\Smarty\Template::$cache_id) + * + * @var string + */ + public $cache_id = null; + + /** + * saved cache lifetime in seconds + * + * @var int + */ + public $cache_lifetime = 0; + + /** + * Id for cache locking + * + * @var string + */ + public $lock_id = null; + + /** + * flag that cache is locked by this instance + * + * @var bool + */ + public $is_locked = false; + + /** + * Source Object + * + * @var Source + */ + public $source = null; + + /** + * Nocache hash codes of processed compiled templates + * + * @var array + */ + public $hashes = []; + + /** + * Content buffer + * + * @var string + */ + public $content = null; + + /** + * create Cached Object container + * + * @param Source $source + * @param \Smarty\Cacheresource\Base $handler + * @param $compile_id + * @param $cache_id + */ + public function __construct(Source $source, \Smarty\Cacheresource\Base $handler, $compile_id, $cache_id) { + $this->compile_id = $compile_id; + $this->cache_id = $cache_id; + $this->source = $source; + $this->handler = $handler; + } + + /** + * Render cache template + * + * @param \Smarty\Template $_template + * @param bool $no_output_filter + * + * @throws \Exception + */ + public function render(Template $_template, $no_output_filter = true) { + + if (!$this->isCached($_template)) { + $this->updateCache($_template, $no_output_filter); + } else { + if (!$this->processed) { + $this->process($_template); + } + } + + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->start_cache($_template); + } + + $this->getRenderedTemplateCode($_template, $this->unifunc); + + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->end_cache($_template); + } + + } + + /** + * Check if cache is valid, lock cache if required + * + * @param Template $_template + * + * @return bool flag true if cache is valid + * @throws Exception + */ + public function isCached(Template $_template) { + if ($this->valid !== null) { + return $this->valid; + } + while (true) { + while (true) { + if ($this->exists === false || $_template->getSmarty()->force_compile || $_template->getSmarty()->force_cache) { + $this->valid = false; + } else { + $this->valid = true; + } + if ($this->valid && $_template->caching === \Smarty\Smarty::CACHING_LIFETIME_CURRENT + && $_template->cache_lifetime >= 0 && time() > ($this->timestamp + $_template->cache_lifetime) + ) { + // lifetime expired + $this->valid = false; + } + if ($this->valid && $_template->compile_check === \Smarty\Smarty::COMPILECHECK_ON + && $_template->getSource()->getTimeStamp() > $this->timestamp + ) { + $this->valid = false; + } + if ($this->valid || !$_template->getSmarty()->cache_locking) { + break; + } + if (!$this->handler->locked($_template->getSmarty(), $this)) { + $this->handler->acquireLock($_template->getSmarty(), $this); + break 2; + } + $this->handler->populate($this, $_template); + } + if ($this->valid) { + if (!$_template->getSmarty()->cache_locking || $this->handler->locked($_template->getSmarty(), $this) === null) { + // load cache file for the following checks + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->start_cache($_template); + } + if ($this->handler->process($_template, $this) === false) { + $this->valid = false; + } else { + $this->processed = true; + } + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->end_cache($_template); + } + } else { + $this->is_locked = true; + continue; + } + } else { + return $this->valid; + } + if ($this->valid && $_template->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED + && $_template->getCached()->cache_lifetime >= 0 + && (time() > ($_template->getCached()->timestamp + $_template->getCached()->cache_lifetime)) + ) { + $this->valid = false; + } + if ($_template->getSmarty()->cache_locking) { + if (!$this->valid) { + $this->handler->acquireLock($_template->getSmarty(), $this); + } elseif ($this->is_locked) { + $this->handler->releaseLock($_template->getSmarty(), $this); + } + } + return $this->valid; + } + return $this->valid; + } + + /** + * Process cached template + * + * @param Template $_template template object + */ + private function process(Template $_template) { + if ($this->handler->process($_template, $this) === false) { + $this->valid = false; + } + $this->processed = $this->valid; + } + + /** + * Read cache content from handler + * + * @param Template $_template template object + * + * @return string|false content + */ + public function readCache(Template $_template) { + if (!$_template->getSource()->handler->recompiled) { + return $this->handler->retrieveCachedContent($_template); + } + return false; + } + + /** + * Write this cache object to handler + * + * @param string $content content to cache + * + * @return bool success + */ + public function writeCache(Template $_template, $content) { + if (!$_template->getSource()->handler->recompiled) { + if ($this->handler->storeCachedContent($_template, $content)) { + $this->content = null; + $this->timestamp = time(); + $this->exists = true; + $this->valid = true; + $this->cache_lifetime = $_template->cache_lifetime; + $this->processed = false; + if ($_template->getSmarty()->cache_locking) { + $this->handler->releaseLock($_template->getSmarty(), $this); + } + return true; + } + $this->content = null; + $this->timestamp = false; + $this->exists = false; + $this->valid = false; + $this->processed = false; + } + return false; + } + + /** + * Cache was invalid , so render from compiled and write to cache + * + * @param Template $_template + * @param bool $no_output_filter + * + * @throws \Smarty\Exception + */ + private function updateCache(Template $_template, $no_output_filter) { + + ob_start(); + + $_template->getCompiled()->render($_template); + + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->start_cache($_template); + } + + $this->removeNoCacheHash($_template, $no_output_filter); + $this->process($_template); + + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->end_cache($_template); + } + } + + /** + * Sanitize content and write it to cache resource + * + * @param Template $_template + * @param bool $no_output_filter + * + * @throws \Smarty\Exception + */ + private function removeNoCacheHash(Template $_template, $no_output_filter) { + $php_pattern = '/(<%|%>|<\?php|<\?|\?>|)/'; + $content = ob_get_clean(); + $hash_array = $this->hashes; + $hash_array[$_template->getCompiled()->nocache_hash] = true; + $hash_array = array_keys($hash_array); + $nocache_hash = '(' . implode('|', $hash_array) . ')'; + $_template->getCached()->setNocacheCode(false); + // get text between non-cached items + $cache_split = + preg_split( + "!/\*%%SmartyNocache:{$nocache_hash}%%\*\/(.+?)/\*/%%SmartyNocache:{$nocache_hash}%%\*/!s", + $content + ); + // get non-cached items + preg_match_all( + "!/\*%%SmartyNocache:{$nocache_hash}%%\*\/(.+?)/\*/%%SmartyNocache:{$nocache_hash}%%\*/!s", + $content, + $cache_parts + ); + $content = ''; + // loop over items, stitch back together + foreach ($cache_split as $curr_idx => $curr_split) { + if (preg_match($php_pattern, $curr_split)) { + // escape PHP tags in template content + $php_split = preg_split( + $php_pattern, + $curr_split + ); + preg_match_all( + $php_pattern, + $curr_split, + $php_parts + ); + foreach ($php_split as $idx_php => $curr_php) { + $content .= $curr_php; + if (isset($php_parts[0][$idx_php])) { + $content .= "\n"; + } + } + } else { + $content .= $curr_split; + } + if (isset($cache_parts[0][$curr_idx])) { + $_template->getCached()->setNocacheCode(true); + $content .= $cache_parts[2][$curr_idx]; + } + } + if ( + !$no_output_filter + && !$_template->getCached()->getNocacheCode() + ) { + $content = $_template->getSmarty()->runOutputFilters($content, $_template); + } + + $codeframe = (new \Smarty\Compiler\CodeFrame($_template))->create($content, '', true); + $this->writeCache($_template, $codeframe); + } + + /** + * @return Source|null + */ + public function getSource(): ?Source { + return $this->source; + } + + /** + * @param Source|null $source + */ + public function setSource(?Source $source): void { + $this->source = $source; + } + + /** + * Returns the generated content + * + * @param Template $template + * + * @return string|null + * @throws \Exception + */ + public function getContent(Template $template) { + ob_start(); + $this->render($template); + return ob_get_clean(); + } + + /** + * This function is executed automatically when a generated file is included + * - Decode saved properties + * - Check if file is valid + * + * @param Template $_template + * @param array $properties special template properties + * + * @return bool flag if compiled or cache file is valid + * @throws Exception + */ + public function isFresh(Template $_template, array $properties): bool { + + // on cache resources other than file check version stored in cache code + if (\Smarty\Smarty::SMARTY_VERSION !== $properties['version']) { + return false; + } + + $is_valid = true; + + if (!empty($properties['file_dependency']) && ($_template->compile_check === \Smarty\Smarty::COMPILECHECK_ON)) { + $is_valid = $this->checkFileDependencies($properties['file_dependency'], $_template); + } + + // CACHING_LIFETIME_SAVED cache expiry has to be validated here since otherwise we'd define the unifunc + if ($_template->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED && $properties['cache_lifetime'] >= 0 + && (time() > ($this->timestamp + $properties['cache_lifetime'])) + ) { + $is_valid = false; + } + + $this->cache_lifetime = $properties['cache_lifetime']; + $this->setValid($is_valid); + + if ($is_valid) { + $this->unifunc = $properties['unifunc']; + $this->setNocacheCode($properties['has_nocache_code']); + $this->file_dependency = $properties['file_dependency']; + } + return $is_valid && !function_exists($properties['unifunc']); + } + +} diff --git a/src/Template/Compiled.php b/src/Template/Compiled.php new file mode 100644 index 0000000..caee87f --- /dev/null +++ b/src/Template/Compiled.php @@ -0,0 +1,302 @@ +getSource()->handler->supportsCompiledTemplates()) { + $compiled->populateCompiledFilepath($_template); + } + return $compiled; + } + + /** + * populate Compiled Object with compiled filepath + * + * @param Template $_template template object + **/ + private function populateCompiledFilepath(Template $_template) { + $source = $_template->getSource(); + $smarty = $_template->getSmarty(); + $this->filepath = $smarty->getCompileDir(); + if (isset($_template->compile_id)) { + $this->filepath .= preg_replace('![^\w]+!', '_', $_template->compile_id) . + ($smarty->use_sub_dirs ? DIRECTORY_SEPARATOR : '^'); + } + // if use_sub_dirs, break file into directories + if ($smarty->use_sub_dirs) { + $this->filepath .= $source->uid[0] . $source->uid[1] . DIRECTORY_SEPARATOR . $source->uid[2] . + $source->uid[3] . DIRECTORY_SEPARATOR . $source->uid[4] . $source->uid[5] . + DIRECTORY_SEPARATOR; + } + $this->filepath .= $source->uid . '_'; + if ($source->isConfig) { + $this->filepath .= (int)$smarty->config_read_hidden + (int)$smarty->config_booleanize * 2 + + (int)$smarty->config_overwrite * 4; + } else { + $this->filepath .= (int)$smarty->escape_html * 2; + } + $this->filepath .= '.' . $source->type . '_' . $source->getBasename(); + + if ($_template->caching) { + $this->filepath .= '.cache'; + } + $this->filepath .= '.php'; + $this->timestamp = $this->exists = is_file($this->filepath); + if ($this->exists) { + $this->timestamp = filemtime($this->filepath); + } + } + + /** + * render compiled template code + * + * @param Template $_template + * + * @return string + * @throws \Smarty\Exception + */ + public function render(Template $_template) { + + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->start_render($_template); + } + if (!$this->processed) { + $this->compileAndLoad($_template); + } + + // @TODO Can't Cached handle this? Maybe introduce an event to decouple. + if ($_template->caching) { + $_template->getCached()->file_dependency = + array_merge($_template->getCached()->file_dependency, $this->file_dependency); + } + + $this->getRenderedTemplateCode($_template, $this->unifunc); + + // @TODO Can't Cached handle this? Maybe introduce an event to decouple and remove the $_template->caching property. + if ($_template->caching && $this->getNocacheCode()) { + $_template->getCached()->hashes[$this->nocache_hash] = true; + } + + if ($_template->getSmarty()->debugging) { + $_template->getSmarty()->getDebug()->end_render($_template); + } + } + + /** + * load compiled template or compile from source + * + * @param Template $_smarty_tpl do not change variable name, is used by compiled template + * + * @throws Exception + */ + private function compileAndLoad(Template $_smarty_tpl) { + + if ($_smarty_tpl->getSource()->handler->recompiled) { + $this->recompile($_smarty_tpl); + return; + } + + if ($this->exists && !$_smarty_tpl->getSmarty()->force_compile + && !($_smarty_tpl->compile_check && $_smarty_tpl->getSource()->getTimeStamp() > $this->getTimeStamp()) + ) { + $this->loadCompiledTemplate($_smarty_tpl); + } + + if (!$this->isValid) { + $this->compileAndWrite($_smarty_tpl); + $this->loadCompiledTemplate($_smarty_tpl); + } + + $this->processed = true; + } + + /** + * compile template from source + * + * @param Template $_smarty_tpl do not change variable name, is used by compiled template + * + * @throws Exception + */ + private function recompile(Template $_smarty_tpl) { + $level = ob_get_level(); + ob_start(); + // call compiler + try { + eval('?>' . $this->doCompile($_smarty_tpl)); + } catch (\Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + throw $e; + } + ob_get_clean(); + $this->timestamp = time(); + $this->exists = true; + } + + /** + * compile template from source + * + * @param Template $_template + * + * @throws Exception + */ + public function compileAndWrite(Template $_template) { + // compile locking + if ($saved_timestamp = (!$_template->getSource()->handler->recompiled && is_file($this->filepath))) { + $saved_timestamp = $this->getTimeStamp(); + touch($this->filepath); + } + // compile locking + try { + // call compiler + $this->write($_template, $this->doCompile($_template)); + } catch (\Exception $e) { + // restore old timestamp in case of error + if ($saved_timestamp && is_file($this->filepath)) { + touch($this->filepath, $saved_timestamp); + } + throw $e; + } + } + + /** + * Do the actual compiling. + * + * @param Template $_smarty_tpl + * + * @return string + * @throws Exception + */ + private function doCompile(Template $_smarty_tpl): string { + $this->file_dependency = []; + $this->includes = []; + $this->nocache_hash = null; + $this->unifunc = null; + return $_smarty_tpl->getCompiler()->compileTemplate($_smarty_tpl); + } + + /** + * Write compiled code by handler + * + * @param Template $_template template object + * @param string $code compiled code + * + * @return bool success + * @throws \Smarty\Exception + */ + private function write(Template $_template, $code) { + if (!$_template->getSource()->handler->recompiled) { + if ($_template->getSmarty()->writeFile($this->filepath, $code) === true) { + $this->timestamp = $this->exists = is_file($this->filepath); + if ($this->exists) { + $this->timestamp = filemtime($this->filepath); + return true; + } + } + return false; + } + return true; + } + + /** + * Load fresh compiled template by including the PHP file + * HHVM requires a workaround because of a PHP incompatibility + * + * @param Template $_smarty_tpl do not change/remove variable name, is used by compiled template + * + */ + private function loadCompiledTemplate(Template $_smarty_tpl) { + + if (function_exists('opcache_invalidate') + && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1) + ) { + opcache_invalidate($this->filepath, true); + } elseif (function_exists('apc_compile_file')) { + apc_compile_file($this->filepath); + } + if (defined('HHVM_VERSION')) { + eval('?>' . file_get_contents($this->filepath)); + } else { + include $this->filepath; + } + + } + + /** + * This function is executed automatically when a compiled or cached template file is included + * - Decode saved properties from compiled template and cache files + * - Check if compiled or cache file is valid + * + * @param Template $_template + * @param array $properties special template properties + * + * @return bool flag if compiled or cache file is valid + * @throws Exception + */ + public function isFresh(Template $_template, array $properties): bool { + + // on cache resources other than file check version stored in cache code + if (\Smarty\Smarty::SMARTY_VERSION !== $properties['version']) { + return false; + } + + $is_valid = true; + if (!empty($properties['file_dependency']) && $_template->compile_check) { + $is_valid = $this->checkFileDependencies($properties['file_dependency'], $_template); + } + + $this->isValid = $is_valid; + $this->includes = $properties['includes'] ?? []; + + if ($is_valid) { + $this->unifunc = $properties['unifunc']; + $this->setNocacheCode($properties['has_nocache_code']); + $this->file_dependency = $properties['file_dependency']; + } + return $is_valid && !function_exists($properties['unifunc']); + } + + /** + * This method is here only to fix an issue when upgrading from Smarty v4 to v5. + */ + public function _decodeProperties($a, $b, $c = false): bool { return false; } + +} diff --git a/src/Template/Config.php b/src/Template/Config.php new file mode 100644 index 0000000..b2fcbf8 --- /dev/null +++ b/src/Template/Config.php @@ -0,0 +1,36 @@ + true]; + + public function createCompiler(): \Smarty\Compiler\BaseCompiler { + return new \Smarty\Compiler\Configfile($this->smarty); + } + + protected static function getDefaultHandlerFunc(Smarty $smarty) { + return $smarty->default_config_handler_func; + } +} diff --git a/src/Template/GeneratedPhpFile.php b/src/Template/GeneratedPhpFile.php new file mode 100644 index 0000000..f436e97 --- /dev/null +++ b/src/Template/GeneratedPhpFile.php @@ -0,0 +1,159 @@ +exists && !$this->timestamp) { + $this->timestamp = filemtime($this->filepath); + } + return $this->timestamp; + } + + /** + * @return bool + */ + public function getNocacheCode(): bool { + return $this->has_nocache_code; + } + + /** + * @param bool $has_nocache_code + */ + public function setNocacheCode(bool $has_nocache_code): void { + $this->has_nocache_code = $has_nocache_code; + } + + /** + * get rendered template content by calling compiled or cached template code + * + * @param string $unifunc function with template code + * + * @throws \Exception + */ + protected function getRenderedTemplateCode(\Smarty\Template $_template, $unifunc) { + $level = ob_get_level(); + try { + if (empty($unifunc) || !function_exists($unifunc)) { + throw new \Smarty\Exception("Invalid compiled template for '{$this->filepath}'"); + } + $unifunc($_template); + } catch (\Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + } + + /** + * @param $file_dependency + * @param Template $_template + * + * @return bool + * @throws Exception + */ + protected function checkFileDependencies($file_dependency, Template $_template): bool { + // check file dependencies at compiled code + foreach ($file_dependency as $_file_to_check) { + + $handler = \Smarty\Resource\BasePlugin::load($_template->getSmarty(), $_file_to_check[2]); + + if ($handler instanceof FilePlugin) { + if ($_template->getSource()->getResourceName() === $_file_to_check[0]) { + // do not recheck current template + continue; + } + $mtime = $handler->getResourceNameTimestamp($_file_to_check[0], $_template->getSmarty(), $_template->getSource()->isConfig); + } else { + + if ($handler->checkTimestamps()) { + // @TODO this doesn't actually check any dependencies, but only the main source file + // and that might to be irrelevant, as the comment "do not recheck current template" above suggests + $source = Source::load($_template, $_template->getSmarty()); + $mtime = $source->getTimeStamp(); + } else { + continue; + } + } + + if ($mtime === false || $mtime > $_file_to_check[1]) { + return false; + } + } + return true; + } + +} diff --git a/src/Template/Source.php b/src/Template/Source.php new file mode 100644 index 0000000..382e710 --- /dev/null +++ b/src/Template/Source.php @@ -0,0 +1,285 @@ +handler = \Smarty\Resource\BasePlugin::load($smarty, $type); + + $this->smarty = $smarty; + $this->resource = $type . ':' . $name; + $this->type = $type; + $this->name = $name; + } + + /** + * initialize Source Object for given resource + * Either [$_template] or [$smarty, $template_resource] must be specified + * + * @param Template|null $_template template object + * @param Smarty|null $smarty smarty object + * @param null $template_resource resource identifier + * + * @return Source Source Object + * @throws Exception + */ + public static function load( + Template $_template = null, + Smarty $smarty = null, + $template_resource = null + ) { + if ($_template) { + $smarty = $_template->getSmarty(); + $template_resource = $_template->template_resource; + } + if (empty($template_resource)) { + throw new Exception('Source: Missing name'); + } + // parse resource_name, load resource handler, identify unique resource name + if (preg_match('/^([A-Za-z0-9_\-]{2,}):([\s\S]*)$/', $template_resource, $match)) { + $type = $match[1]; + $name = $match[2]; + } else { + // no resource given, use default + // or single character before the colon is not a resource type, but part of the filepath + $type = $smarty->default_resource_type; + $name = $template_resource; + } + + if (isset(self::$_incompatible_resources[$type])) { + throw new Exception("Unable to use resource '{$type}' for " . __METHOD__); + } + + // create new source object + $source = new static($smarty, $type, $name); + $source->handler->populate($source, $_template); + if (!$source->exists && static::getDefaultHandlerFunc($smarty)) { + $source->_getDefaultTemplate(static::getDefaultHandlerFunc($smarty)); + $source->handler->populate($source, $_template); + } + return $source; + } + + protected static function getDefaultHandlerFunc(Smarty $smarty) { + return $smarty->default_template_handler_func; + } + + /** + * Get source time stamp + * + * @return int + */ + public function getTimeStamp() { + if (!isset($this->timestamp)) { + $this->handler->populateTimestamp($this); + } + return $this->timestamp; + } + + /** + * Get source content + * + * @return string + * @throws \Smarty\Exception + */ + public function getContent() { + return $this->content ?? $this->handler->getContent($this); + } + + /** + * get default content from template or config resource handler + * + * @throws \Smarty\Exception + */ + public function _getDefaultTemplate($default_handler) { + $_content = $_timestamp = null; + $_return = call_user_func_array( + $default_handler, + [$this->type, $this->name, &$_content, &$_timestamp, $this->smarty] + ); + if (is_string($_return)) { + $this->exists = is_file($_return); + if ($this->exists) { + $this->timestamp = filemtime($_return); + } else { + throw new Exception( + 'Default handler: Unable to load ' . + "default file '{$_return}' for '{$this->type}:{$this->name}'" + ); + } + $this->name = $_return; + $this->uid = sha1($_return); + } elseif ($_return === true) { + $this->content = $_content; + $this->exists = true; + $this->uid = $this->name = sha1($_content); + $this->handler = \Smarty\Resource\BasePlugin::load($this->smarty, 'eval'); + } else { + $this->exists = false; + throw new Exception( + 'Default handler: No ' . ($this->isConfig ? 'config' : 'template') . + " default content for '{$this->type}:{$this->name}'" + ); + } + } + + public function createCompiler(): \Smarty\Compiler\BaseCompiler { + return new \Smarty\Compiler\Template($this->smarty); + } + + public function getSmarty() { + return $this->smarty; + } + + /** + * Determine basename for compiled filename + * + * @return string resource's basename + */ + public function getBasename() + { + return $this->handler->getBasename($this); + } + + /** + * Return source name + * e.g.: 'sub/index.tpl' + * + * @return string + */ + public function getResourceName(): string { + return (string) $this->name; + } + + /** + * Return source name, including the type prefix. + * e.g.: 'file:sub/index.tpl' + * + * @return string + */ + public function getFullResourceName(): string { + return $this->type . ':' . $this->name; + } + + public function getFilepath(): ?string { + if ($this->handler instanceof FilePlugin) { + return $this->handler->getFilePath($this->name, $this->smarty, $this->isConfig); + } + return null; + } + + public function isConfig(): bool { + return $this->isConfig; + } + +} diff --git a/src/TemplateBase.php b/src/TemplateBase.php new file mode 100644 index 0000000..3674edf --- /dev/null +++ b/src/TemplateBase.php @@ -0,0 +1,425 @@ +getSmarty(); + // test if allowed methods callable + if (!empty($allowed_methods_properties)) { + foreach ((array)$allowed_methods_properties as $method) { + if (!is_callable([$object, $method]) && !property_exists($object, $method)) { + throw new Exception("Undefined method or property '$method' in registered object"); + } + } + } + // test if block methods callable + if (!empty($block_methods)) { + foreach ((array)$block_methods as $method) { + if (!is_callable([$object, $method])) { + throw new Exception("Undefined method '$method' in registered object"); + } + } + } + // register the object + $smarty->registered_objects[$object_name] = + [$object, (array)$allowed_methods_properties, (boolean)$format, (array)$block_methods]; + return $this; + } + + /** + * Registers plugin to be used in templates + * + * @param string $object_name name of object + * + * @return static + * @api Smarty::unregisterObject() + * + */ + public function unregisterObject($object_name) { + $smarty = $this->getSmarty(); + if (isset($smarty->registered_objects[$object_name])) { + unset($smarty->registered_objects[$object_name]); + } + return $this; + } + + /** + * @return int + */ + public function getCompileCheck(): int { + return $this->compile_check; + } + + /** + * @param int $compile_check + */ + public function setCompileCheck($compile_check) { + $this->compile_check = (int)$compile_check; + } + + /** + * @param int $caching + */ + public function setCaching($caching) { + $this->caching = (int)$caching; + } + + /** + * @param int $cache_lifetime + */ + public function setCacheLifetime($cache_lifetime) { + $this->cache_lifetime = $cache_lifetime; + } + + /** + * @param string $compile_id + */ + public function setCompileId($compile_id) { + $this->compile_id = $compile_id; + } + + /** + * @param string $cache_id + */ + public function setCacheId($cache_id) { + $this->cache_id = $cache_id; + } + + /** + * creates a data object + * + * @param Data|null $parent next higher level of Smarty + * variables + * @param null $name optional data block name + * + * @return Data data object + * @throws Exception + * @api Smarty::createData() + * + */ + public function createData(Data $parent = null, $name = null) { + /* @var Smarty $smarty */ + $smarty = $this->getSmarty(); + $dataObj = new Data($parent, $smarty, $name); + if ($smarty->debugging) { + $smarty->getDebug()->register_data($dataObj); + } + return $dataObj; + } + + /** + * return name of debugging template + * + * @return string + * @api Smarty::getDebugTemplate() + * + */ + public function getDebugTemplate() { + $smarty = $this->getSmarty(); + return $smarty->debug_tpl; + } + + /** + * @return Debug + */ + public function getDebug(): Debug { + if (!isset($this->debug)) { + $this->debug = new \Smarty\Debug(); + } + return $this->debug; + } + + + /** + * return a reference to a registered object + * + * @param string $object_name object name + * + * @return object + * @throws \Smarty\Exception if no such object is found + * + * @api Smarty::getRegisteredObject() + */ + public function getRegisteredObject($object_name) { + $smarty = $this->getSmarty(); + if (!isset($smarty->registered_objects[$object_name])) { + throw new Exception("'$object_name' is not a registered object"); + } + if (!is_object($smarty->registered_objects[$object_name][0])) { + throw new Exception("registered '$object_name' is not an object"); + } + return $smarty->registered_objects[$object_name][0]; + } + + /** + * Get literals + * + * @return array list of literals + * @api Smarty::getLiterals() + * + */ + public function getLiterals() { + $smarty = $this->getSmarty(); + return (array)$smarty->literals; + } + + /** + * Add literals + * + * @param array|string $literals literal or list of literals + * to addto add + * + * @return static + * @throws \Smarty\Exception + * @api Smarty::addLiterals() + * + */ + public function addLiterals($literals = null) { + if (isset($literals)) { + $this->_setLiterals($this->getSmarty(), (array)$literals); + } + return $this; + } + + /** + * Set literals + * + * @param array|string $literals literal or list of literals + * to setto set + * + * @return static + * @throws \Smarty\Exception + * @api Smarty::setLiterals() + * + */ + public function setLiterals($literals = null) { + $smarty = $this->getSmarty(); + $smarty->literals = []; + if (!empty($literals)) { + $this->_setLiterals($smarty, (array)$literals); + } + return $this; + } + + /** + * common setter for literals for easier handling of duplicates the + * Smarty::$literals array gets filled with identical key values + * + * @param Smarty $smarty + * @param array $literals + * + * @throws \Smarty\Exception + */ + private function _setLiterals(Smarty $smarty, $literals) { + $literals = array_combine($literals, $literals); + $error = isset($literals[$smarty->getLeftDelimiter()]) ? [$smarty->getLeftDelimiter()] : []; + $error = isset($literals[$smarty->getRightDelimiter()]) ? $error[] = $smarty->getRightDelimiter() : $error; + if (!empty($error)) { + throw new Exception( + 'User defined literal(s) "' . $error . + '" may not be identical with left or right delimiter' + ); + } + $smarty->literals = array_merge((array)$smarty->literals, (array)$literals); + } + + /** + * Registers static classes to be used in templates + * + * @param string $class_name + * @param string $class_impl the referenced PHP class to + * register + * + * @return static + * @throws \Smarty\Exception + * @api Smarty::registerClass() + * + */ + public function registerClass($class_name, $class_impl) { + $smarty = $this->getSmarty(); + // test if exists + if (!class_exists($class_impl)) { + throw new Exception("Undefined class '$class_impl' in register template class"); + } + // register the class + $smarty->registered_classes[$class_name] = $class_impl; + return $this; + } + + /** + * Register config default handler + * + * @param callable $callback class/method name + * + * @return static + * @throws Exception if $callback is not callable + * @api Smarty::registerDefaultConfigHandler() + * + */ + public function registerDefaultConfigHandler($callback) { + $smarty = $this->getSmarty(); + if (is_callable($callback)) { + $smarty->default_config_handler_func = $callback; + } else { + throw new Exception('Default config handler not callable'); + } + return $this; + } + + /** + * Register template default handler + * + * @param callable $callback class/method name + * + * @return static + * @throws Exception if $callback is not callable + * @api Smarty::registerDefaultTemplateHandler() + * + */ + public function registerDefaultTemplateHandler($callback) { + $smarty = $this->getSmarty(); + if (is_callable($callback)) { + $smarty->default_template_handler_func = $callback; + } else { + throw new Exception('Default template handler not callable'); + } + return $this; + } + + /** + * Registers a resource to fetch a template + * + * @param string $name name of resource type + * @param \Smarty\Resource\BasePlugin $resource_handler instance of Smarty\Resource\BasePlugin + * + * @return static + * + * @api Smarty::registerResource() + */ + public function registerResource($name, \Smarty\Resource\BasePlugin $resource_handler) { + $smarty = $this->getSmarty(); + $smarty->registered_resources[$name] = $resource_handler; + return $this; + } + + /** + * Unregisters a resource to fetch a template + * + * @param string $type name of resource type + * + * @return static + * @api Smarty::unregisterResource() + * + */ + public function unregisterResource($type) { + $smarty = $this->getSmarty(); + if (isset($smarty->registered_resources[$type])) { + unset($smarty->registered_resources[$type]); + } + return $this; + } + + /** + * set the debug template + * + * @param string $tpl_name + * + * @return static + * @throws Exception if file is not readable + * @api Smarty::setDebugTemplate() + * + */ + public function setDebugTemplate($tpl_name) { + $smarty = $this->getSmarty(); + if (!is_readable($tpl_name)) { + throw new Exception("Unknown file '{$tpl_name}'"); + } + $smarty->debug_tpl = $tpl_name; + return $this; + } + + + +} diff --git a/src/TestInstall.php b/src/TestInstall.php new file mode 100644 index 0000000..e24c398 --- /dev/null +++ b/src/TestInstall.php @@ -0,0 +1,211 @@ +\n"; + echo "Smarty Installation test...\n"; + echo "Testing template directory...\n"; + } + $_stream_resolve_include_path = function_exists('stream_resolve_include_path'); + // test if all registered template_dir are accessible + foreach ($smarty->getTemplateDir() as $template_dir) { + $_template_dir = $template_dir; + $template_dir = realpath($template_dir); + // resolve include_path or fail existence + if (!$template_dir) { + $status = false; + $message = "FAILED: $_template_dir does not exist"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'template_dir' ] = $message; + } + continue; + } + if (!is_dir($template_dir)) { + $status = false; + $message = "FAILED: $template_dir is not a directory"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'template_dir' ] = $message; + } + } elseif (!is_readable($template_dir)) { + $status = false; + $message = "FAILED: $template_dir is not readable"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'template_dir' ] = $message; + } + } else { + if ($errors === null) { + echo "$template_dir is OK.\n"; + } + } + } + if ($errors === null) { + echo "Testing compile directory...\n"; + } + // test if registered compile_dir is accessible + $__compile_dir = $smarty->getCompileDir(); + $_compile_dir = realpath($__compile_dir); + if (!$_compile_dir) { + $status = false; + $message = "FAILED: {$__compile_dir} does not exist"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'compile_dir' ] = $message; + } + } elseif (!is_dir($_compile_dir)) { + $status = false; + $message = "FAILED: {$_compile_dir} is not a directory"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'compile_dir' ] = $message; + } + } elseif (!is_readable($_compile_dir)) { + $status = false; + $message = "FAILED: {$_compile_dir} is not readable"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'compile_dir' ] = $message; + } + } elseif (!is_writable($_compile_dir)) { + $status = false; + $message = "FAILED: {$_compile_dir} is not writable"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'compile_dir' ] = $message; + } + } else { + if ($errors === null) { + echo "{$_compile_dir} is OK.\n"; + } + } + if ($errors === null) { + echo "Testing plugins directory...\n"; + } + if ($errors === null) { + echo "Testing cache directory...\n"; + } + // test if all registered cache_dir is accessible + $__cache_dir = $smarty->getCacheDir(); + $_cache_dir = realpath($__cache_dir); + if (!$_cache_dir) { + $status = false; + $message = "FAILED: {$__cache_dir} does not exist"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'cache_dir' ] = $message; + } + } elseif (!is_dir($_cache_dir)) { + $status = false; + $message = "FAILED: {$_cache_dir} is not a directory"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'cache_dir' ] = $message; + } + } elseif (!is_readable($_cache_dir)) { + $status = false; + $message = "FAILED: {$_cache_dir} is not readable"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'cache_dir' ] = $message; + } + } elseif (!is_writable($_cache_dir)) { + $status = false; + $message = "FAILED: {$_cache_dir} is not writable"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'cache_dir' ] = $message; + } + } else { + if ($errors === null) { + echo "{$_cache_dir} is OK.\n"; + } + } + if ($errors === null) { + echo "Testing configs directory...\n"; + } + // test if all registered config_dir are accessible + foreach ($smarty->getConfigDir() as $config_dir) { + $_config_dir = $config_dir; + // resolve include_path or fail existence + if (!$config_dir) { + $status = false; + $message = "FAILED: $_config_dir does not exist"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'config_dir' ] = $message; + } + continue; + } + if (!is_dir($config_dir)) { + $status = false; + $message = "FAILED: $config_dir is not a directory"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'config_dir' ] = $message; + } + } elseif (!is_readable($config_dir)) { + $status = false; + $message = "FAILED: $config_dir is not readable"; + if ($errors === null) { + echo $message . ".\n"; + } else { + $errors[ 'config_dir' ] = $message; + } + } else { + if ($errors === null) { + echo "$config_dir is OK.\n"; + } + } + } + if ($errors === null) { + echo "Tests complete.\n"; + echo "\n"; + } + return $status; + } +} diff --git a/src/UndefinedVariable.php b/src/UndefinedVariable.php new file mode 100644 index 0000000..53f13f4 --- /dev/null +++ b/src/UndefinedVariable.php @@ -0,0 +1,19 @@ +value = $value; + } + + /** + * if true any output of this variable will be not cached + * + * @var boolean + */ + private $nocache = false; + + /** + * @param bool $nocache + */ + public function setNocache(bool $nocache): void { + $this->nocache = $nocache; + } + + /** + * create Smarty variable object + * + * @param mixed $value the value to assign + * @param boolean $nocache if true any output of this variable will be not cached + */ + public function __construct($value = null, $nocache = false) + { + $this->value = $value; + $this->nocache = $nocache; + } + + public function getValue() { + return $this->value; + } + + /** + * <> String conversion + * + * @return string + */ + public function __toString() + { + return (string)$this->value; + } + + /** + * Handles ++$a and --$a in templates. + * + * @param $operator '++' or '--', defaults to '++' + * + * @return int|mixed + * @throws Exception + */ + public function preIncDec($operator = '++') { + if ($operator == '--') { + return --$this->value; + } elseif ($operator == '++') { + return ++$this->value; + } else { + throw new Exception("Invalid incdec operator. Use '--' or '++'."); + } + return $this->value; + } + + /** + * Handles $a++ and $a-- in templates. + * + * @param $operator '++' or '--', defaults to '++' + * + * @return int|mixed + * @throws Exception + */ + public function postIncDec($operator = '++') { + if ($operator == '--') { + return $this->value--; + } elseif ($operator == '++') { + return $this->value++; + } else { + throw new Exception("Invalid incdec operator. Use '--' or '++'."); + } + } + + /** + * @return bool + */ + public function isNocache(): bool { + return $this->nocache; + } + +} diff --git a/src/bootstrap.php b/src/bootstrap.php deleted file mode 100644 index a226ac0..0000000 --- a/src/bootstrap.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -/** - * Load and register Smarty Autoloader - */ -if (!class_exists('Smarty_Autoloader')) { - include __DIR__ . '/Autoloader.php'; -} -Smarty_Autoloader::register(true); diff --git a/src/debug.tpl b/src/debug.tpl index cd93256..3dd25bf 100644 --- a/src/debug.tpl +++ b/src/debug.tpl @@ -1,4 +1,4 @@ -{capture name='_smarty_debug' assign=debug_output} +{capture name='_smarty_debug' assign='debug_output'} @@ -108,7 +108,7 @@ -

Smarty {Smarty::SMARTY_VERSION} Debug Console +

Smarty {\Smarty\Smarty::SMARTY_VERSION} Debug Console - {if isset($template_name)}{$template_name|debug_print_var nofilter} {/if}{if !empty($template_data)}Total Time {$execution_time|string_format:"%.5f"}{/if}

{if !empty($template_data)} @@ -144,6 +144,7 @@ {$vars['attributes']|debug_print_var nofilter} {/if} + {/foreach} @@ -166,7 +167,7 @@ {/capture} -'; - } elseif ($encode === 'javascript_charcode') { - for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { - $ord[] = ord($string[ $x ]); - } - return ''; - } elseif ($encode === 'hex') { - preg_match('!^(.*)(\?.*)$!', $address, $match); - if (!empty($match[ 2 ])) { - trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.", E_USER_WARNING); - return; - } - $address_encode = ''; - for ($x = 0, $_length = strlen($address); $x < $_length; $x++) { - if (preg_match('!\w!' . Smarty::$_UTF8_MODIFIER, $address[ $x ])) { - $address_encode .= '%' . bin2hex($address[ $x ]); - } else { - $address_encode .= $address[ $x ]; - } - } - $text_encode = ''; - for ($x = 0, $_length = strlen($text); $x < $_length; $x++) { - $text_encode .= '&#x' . bin2hex($text[ $x ]) . ';'; - } - $mailto = "mailto:"; - return '' . $text_encode . ''; - } else { - // no encoding - return $string; - } -} diff --git a/src/plugins/function.math.php b/src/plugins/function.math.php deleted file mode 100644 index 34912d2..0000000 --- a/src/plugins/function.math.php +++ /dev/null @@ -1,142 +0,0 @@ - - * - * @param array $params parameters - * @param Smarty_Internal_Template $template template object - * - * @return string|null - */ -function smarty_function_math($params, $template) -{ - static $_allowed_funcs = - array( - 'int' => true, - 'abs' => true, - 'ceil' => true, - 'acos' => true, - 'acosh' => true, - 'cos' => true, - 'cosh' => true, - 'deg2rad' => true, - 'rad2deg' => true, - 'exp' => true, - 'floor' => true, - 'log' => true, - 'log10' => true, - 'max' => true, - 'min' => true, - 'pi' => true, - 'pow' => true, - 'rand' => true, - 'round' => true, - 'asin' => true, - 'asinh' => true, - 'sin' => true, - 'sinh' => true, - 'sqrt' => true, - 'srand' => true, - 'atan' => true, - 'atanh' => true, - 'tan' => true, - 'tanh' => true - ); - - // be sure equation parameter is present - if (empty($params[ 'equation' ])) { - trigger_error("math: missing equation parameter", E_USER_WARNING); - return; - } - $equation = $params[ 'equation' ]; - - // Remove whitespaces - $equation = preg_replace('/\s+/', '', $equation); - - // Adapted from https://www.php.net/manual/en/function.eval.php#107377 - $number = '-?(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number - $functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))'; - $operators = '[,+\/*\^%-]'; // Allowed math operators - $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)*\)|\((?1)*\)))(?:'.$operators.'(?1))?)+$/'; - - if (!preg_match($regexp, $equation)) { - trigger_error("math: illegal characters", E_USER_WARNING); - return; - } - - // make sure parenthesis are balanced - if (substr_count($equation, '(') !== substr_count($equation, ')')) { - trigger_error("math: unbalanced parenthesis", E_USER_WARNING); - return; - } - - // disallow backticks - if (strpos($equation, '`') !== false) { - trigger_error("math: backtick character not allowed in equation", E_USER_WARNING); - return; - } - - // also disallow dollar signs - if (strpos($equation, '$') !== false) { - trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING); - return; - } - foreach ($params as $key => $val) { - if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') { - // make sure value is not empty - if (strlen($val) === 0) { - trigger_error("math: parameter '{$key}' is empty", E_USER_WARNING); - return; - } - if (!is_numeric($val)) { - trigger_error("math: parameter '{$key}' is not numeric", E_USER_WARNING); - return; - } - } - } - // match all vars in equation, make sure all are passed - preg_match_all('!(?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)!', $equation, $match); - foreach ($match[ 1 ] as $curr_var) { - if ($curr_var && !isset($params[ $curr_var ]) && !isset($_allowed_funcs[ $curr_var ])) { - trigger_error( - "math: function call '{$curr_var}' not allowed, or missing parameter '{$curr_var}'", - E_USER_WARNING - ); - return; - } - } - foreach ($params as $key => $val) { - if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') { - $equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation); - } - } - $smarty_math_result = null; - eval("\$smarty_math_result = " . $equation . ";"); - - if (empty($params[ 'format' ])) { - if (empty($params[ 'assign' ])) { - return $smarty_math_result; - } else { - $template->assign($params[ 'assign' ], $smarty_math_result); - } - } else { - if (empty($params[ 'assign' ])) { - printf($params[ 'format' ], $smarty_math_result); - } else { - $template->assign($params[ 'assign' ], sprintf($params[ 'format' ], $smarty_math_result)); - } - } -} diff --git a/src/plugins/function.popup.php b/src/plugins/function.popup.php deleted file mode 100644 index 3a76b78..0000000 --- a/src/plugins/function.popup.php +++ /dev/null @@ -1,119 +0,0 @@ - - * Name: popup
- * Purpose: make text pop up in windows via overlib - * @link http://smarty.php.net/manual/en/language.function.popup.php {popup} - * (Smarty online manual) - * @author Monte Ohrt - * @param array - * @param Smarty - * @return string - */ -function smarty_function_popup($params, &$smarty) -{ - $append = ''; - foreach ($params as $_key=>$_value) { - switch ($_key) { - case 'text': - case 'trigger': - case 'function': - case 'inarray': - $$_key = (string)$_value; - if ($_key == 'function' || $_key == 'inarray') - $append .= ',' . strtoupper($_key) . ",'$_value'"; - break; - - case 'caption': - case 'closetext': - case 'status': - $append .= ',' . strtoupper($_key) . ",'" . str_replace("'","\'",$_value) . "'"; - break; - - case 'fgcolor': - case 'bgcolor': - case 'textcolor': - case 'capcolor': - case 'closecolor': - case 'textfont': - case 'captionfont': - case 'closefont': - case 'fgbackground': - case 'bgbackground': - case 'caparray': - case 'capicon': - case 'background': - case 'frame': - $append .= ',' . strtoupper($_key) . ",'$_value'"; - break; - - case 'textsize': - case 'captionsize': - case 'closesize': - case 'width': - case 'height': - case 'border': - case 'offsetx': - case 'offsety': - case 'snapx': - case 'snapy': - case 'fixx': - case 'fixy': - case 'padx': - case 'pady': - case 'timeout': - case 'delay': - $append .= ',' . strtoupper($_key) . ",$_value"; - break; - - case 'sticky': - case 'left': - case 'right': - case 'center': - case 'above': - case 'below': - case 'noclose': - case 'autostatus': - case 'autostatuscap': - case 'fullhtml': - case 'hauto': - case 'vauto': - case 'mouseoff': - case 'followmouse': - case 'closeclick': - if ($_value) $append .= ',' . strtoupper($_key); - break; - - default: - $smarty->trigger_error("[popup] unknown parameter $_key", E_USER_WARNING); - } - } - - if (empty($text) && !isset($inarray) && empty($function)) { - $smarty->trigger_error("overlib: attribute 'text' or 'inarray' or 'function' required"); - return false; - } - - if (empty($trigger)) { $trigger = "onmouseover"; } - - $retval = $trigger . '="return overlib(\''.preg_replace(array("!'!","![\r\n]!"),array("\'",'\r'),$text).'\''; - $retval .= $append . ');"'; - if ($trigger == 'onmouseover') - $retval .= ' onmouseout="nd();"'; - - - return $retval; -} - -/* vim: set expandtab: */ - -?> diff --git a/src/plugins/function.popup_init.php b/src/plugins/function.popup_init.php deleted file mode 100644 index 93cb454..0000000 --- a/src/plugins/function.popup_init.php +++ /dev/null @@ -1,40 +0,0 @@ - - * Name: popup_init
- * Purpose: initialize overlib - * @link http://smarty.php.net/manual/en/language.function.popup.init.php {popup_init} - * (Smarty online manual) - * @author Monte Ohrt - * @param array - * @param Smarty - * @return string - */ -function smarty_function_popup_init($params, &$smarty) -{ - $zindex = 1000; - - if (!empty($params['zindex'])) { - $zindex = $params['zindex']; - } - - if (!empty($params['src'])) { - return '' . "\n" - . '' . "\n"; - } else { - $smarty->trigger_error("popup_init: missing src parameter"); - } -} - -/* vim: set expandtab: */ - -?> diff --git a/src/plugins/modifier.capitalize.php b/src/plugins/modifier.capitalize.php deleted file mode 100644 index 2903d61..0000000 --- a/src/plugins/modifier.capitalize.php +++ /dev/null @@ -1,147 +0,0 @@ - - * @author Rodney Rehm - */ -function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) -{ - $string = (string) $string; - - if (Smarty::$_MBSTRING) { - if ($lc_rest) { - // uppercase (including hyphenated words) - $upper_string = mb_convert_case($string, MB_CASE_TITLE, Smarty::$_CHARSET); - } else { - // uppercase word breaks - $upper_string = preg_replace_callback( - "!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, - 'smarty_mod_cap_mbconvert_cb', - $string - ); - } - // check uc_digits case - if (!$uc_digits) { - if (preg_match_all( - "!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, - $string, - $matches, - PREG_OFFSET_CAPTURE - ) - ) { - foreach ($matches[ 1 ] as $match) { - $upper_string = - substr_replace( - $upper_string, - mb_strtolower($match[ 0 ], Smarty::$_CHARSET), - $match[ 1 ], - strlen($match[ 0 ]) - ); - } - } - } - $upper_string = - preg_replace_callback( - "!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, - 'smarty_mod_cap_mbconvert2_cb', - $upper_string - ); - return $upper_string; - } - // lowercase first - if ($lc_rest) { - $string = strtolower($string); - } - // uppercase (including hyphenated words) - $upper_string = - preg_replace_callback( - "!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, - 'smarty_mod_cap_ucfirst_cb', - $string - ); - // check uc_digits case - if (!$uc_digits) { - if (preg_match_all( - "!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, - $string, - $matches, - PREG_OFFSET_CAPTURE - ) - ) { - foreach ($matches[ 1 ] as $match) { - $upper_string = - substr_replace($upper_string, strtolower($match[ 0 ]), $match[ 1 ], strlen($match[ 0 ])); - } - } - } - $upper_string = preg_replace_callback( - "!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, - 'smarty_mod_cap_ucfirst2_cb', - $upper_string - ); - return $upper_string; -} - -/** - * - * Bug: create_function() use exhausts memory when used in long loops - * Fix: use declared functions for callbacks instead of using create_function() - * Note: This can be fixed using anonymous functions instead, but that requires PHP >= 5.3 - * - * @author Kyle Renfrow - */ -/** - * @param $matches - * - * @return string - */ -function smarty_mod_cap_mbconvert_cb($matches) -{ - return stripslashes($matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 2 ]), MB_CASE_UPPER, Smarty::$_CHARSET); -} - -/** - * @param $matches - * - * @return string - */ -function smarty_mod_cap_mbconvert2_cb($matches) -{ - return stripslashes($matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 3 ]), MB_CASE_UPPER, Smarty::$_CHARSET); -} - -/** - * @param $matches - * - * @return string - */ -function smarty_mod_cap_ucfirst_cb($matches) -{ - return stripslashes($matches[ 1 ]) . ucfirst(stripslashes($matches[ 2 ])); -} - -/** - * @param $matches - * - * @return string - */ -function smarty_mod_cap_ucfirst2_cb($matches) -{ - return stripslashes($matches[ 1 ]) . ucfirst(stripslashes($matches[ 3 ])); -} diff --git a/src/plugins/modifier.count.php b/src/plugins/modifier.count.php deleted file mode 100644 index ca35fc1..0000000 --- a/src/plugins/modifier.count.php +++ /dev/null @@ -1,36 +0,0 @@ - Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface, - * > 1 would be returned, unless value was null, in which case 0 would be returned. - */ - - if ($arrayOrObject instanceof Countable || is_array($arrayOrObject)) { - return count($arrayOrObject, (int) $mode); - } elseif ($arrayOrObject === null) { - return 0; - } - return 1; -} diff --git a/src/plugins/modifier.date_format.php b/src/plugins/modifier.date_format.php deleted file mode 100644 index e3589fd..0000000 --- a/src/plugins/modifier.date_format.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * @param string $string input date string - * @param string $format strftime format for output - * @param string $default_date default date if $string is empty - * @param string $formatter either 'strftime' or 'auto' - * - * @return string |void - * @uses smarty_make_timestamp() - */ -function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') -{ - if ($format === null) { - $format = Smarty::$_DATE_FORMAT; - } - /** - * require_once the {@link shared.make_timestamp.php} plugin - */ - static $is_loaded = false; - if (!$is_loaded) { - if (!is_callable('smarty_make_timestamp')) { - include_once SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'; - } - $is_loaded = true; - } - if (!empty($string) && $string !== '0000-00-00' && $string !== '0000-00-00 00:00:00') { - $timestamp = smarty_make_timestamp($string); - } elseif (!empty($default_date)) { - $timestamp = smarty_make_timestamp($default_date); - } else { - return; - } - if ($formatter === 'strftime' || ($formatter === 'auto' && strpos($format, '%') !== false)) { - if (Smarty::$_IS_WINDOWS) { - $_win_from = array( - '%D', - '%h', - '%n', - '%r', - '%R', - '%t', - '%T' - ); - $_win_to = array( - '%m/%d/%y', - '%b', - "\n", - '%I:%M:%S %p', - '%H:%M', - "\t", - '%H:%M:%S' - ); - if (strpos($format, '%e') !== false) { - $_win_from[] = '%e'; - $_win_to[] = sprintf('%\' 2d', date('j', $timestamp)); - } - if (strpos($format, '%l') !== false) { - $_win_from[] = '%l'; - $_win_to[] = sprintf('%\' 2d', date('h', $timestamp)); - } - $format = str_replace($_win_from, $_win_to, $format); - } - // @ to suppress deprecation errors when running in PHP8.1 or higher. - return @strftime($format, $timestamp); - } else { - return date($format, $timestamp); - } -} diff --git a/src/plugins/modifier.debug_print_var.php b/src/plugins/modifier.debug_print_var.php deleted file mode 100644 index 78397d0..0000000 --- a/src/plugins/modifier.debug_print_var.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * @param array|object $var variable to be formatted - * @param int $max maximum recursion depth if $var is an array or object - * @param int $length maximum string length if $var is a string - * @param int $depth actual recursion depth - * @param array $objects processed objects in actual depth to prevent recursive object processing - * - * @return string - */ -function smarty_modifier_debug_print_var($var, $max = 10, $length = 40, $depth = 0, $objects = array()) -{ - $_replace = array("\n" => '\n', "\r" => '\r', "\t" => '\t'); - switch (gettype($var)) { - case 'array': - $results = 'Array (' . count($var) . ')'; - if ($depth === $max) { - break; - } - foreach ($var as $curr_key => $curr_val) { - $results .= '
' . str_repeat(' ', $depth * 2) . '' . strtr($curr_key, $_replace) . - ' => ' . - smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); - $depth--; - } - break; - case 'object': - $object_vars = get_object_vars($var); - $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; - if (in_array($var, $objects)) { - $results .= ' called recursive'; - break; - } - if ($depth === $max) { - break; - } - $objects[] = $var; - foreach ($object_vars as $curr_key => $curr_val) { - $results .= '
' . str_repeat(' ', $depth * 2) . ' ->' . strtr($curr_key, $_replace) . - ' = ' . smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); - $depth--; - } - break; - case 'boolean': - case 'NULL': - case 'resource': - if (true === $var) { - $results = 'true'; - } elseif (false === $var) { - $results = 'false'; - } elseif (null === $var) { - $results = 'null'; - } else { - $results = htmlspecialchars((string)$var); - } - $results = '' . $results . ''; - break; - case 'integer': - case 'float': - $results = htmlspecialchars((string)$var); - break; - case 'string': - $results = strtr($var, $_replace); - if (Smarty::$_MBSTRING) { - if (mb_strlen($var, Smarty::$_CHARSET) > $length) { - $results = mb_substr($var, 0, $length - 3, Smarty::$_CHARSET) . '...'; - } - } else { - if (isset($var[ $length ])) { - $results = substr($var, 0, $length - 3) . '...'; - } - } - $results = htmlspecialchars('"' . $results . '"', ENT_QUOTES, Smarty::$_CHARSET); - break; - case 'unknown type': - default: - $results = strtr((string)$var, $_replace); - if (Smarty::$_MBSTRING) { - if (mb_strlen($results, Smarty::$_CHARSET) > $length) { - $results = mb_substr($results, 0, $length - 3, Smarty::$_CHARSET) . '...'; - } - } else { - if (strlen($results) > $length) { - $results = substr($results, 0, $length - 3) . '...'; - } - } - $results = htmlspecialchars($results, ENT_QUOTES, Smarty::$_CHARSET); - } - return $results; -} diff --git a/src/plugins/modifier.escape.php b/src/plugins/modifier.escape.php deleted file mode 100644 index e168679..0000000 --- a/src/plugins/modifier.escape.php +++ /dev/null @@ -1,189 +0,0 @@ - - * - * @param string $string input string - * @param string $esc_type escape type - * @param string $char_set character set, used for htmlspecialchars() or htmlentities() - * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() - * - * @return string escaped input string - */ -function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) -{ - static $is_loaded_1 = false; - static $is_loaded_2 = false; - if (!$char_set) { - $char_set = Smarty::$_CHARSET; - } - - $string = (string)$string; - - switch ($esc_type) { - case 'html': - return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); - // no break - case 'htmlall': - if (Smarty::$_MBSTRING) { - $string = mb_convert_encoding($string, 'UTF-8', $char_set); - return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode); - } - // no MBString fallback - return htmlentities($string, ENT_QUOTES, $char_set, $double_encode); - // no break - case 'url': - return rawurlencode($string); - case 'urlpathinfo': - return str_replace('%2F', '/', rawurlencode($string)); - case 'quotes': - // escape unescaped single quotes - return preg_replace("%(? '\\\\', - "'" => "\\'", - '"' => '\\"', - "\r" => '\\r', - "\n" => '\\n', - ' '<\/', - // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements - '#is', - $source, - $matches, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER - ) - ) { - foreach ($matches as $match) { - $store[] = $match[ 0 ][ 0 ]; - $_length = strlen($match[ 0 ][ 0 ]); - $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; - $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); - $_offset += $_length - strlen($replace); - $_store++; - } - } - // Strip all HTML-Comments - // yes, even the ones in + HTML + ); + } else { + $smarty->trigger_error("popup_init: missing src parameter"); + } +} + +// __END__ diff --git a/test/src/index.php b/test/src/index.php new file mode 100644 index 0000000..8351985 --- /dev/null +++ b/test/src/index.php @@ -0,0 +1,136 @@ +" . print_r(\CoreLibs\Language\GetLocale::setLocale(), true) . ""; + +$locale = \CoreLibs\Language\GetLocale::setLocale(); +$l10n = new \CoreLibs\Language\L10n( + $locale['locale'], + $locale['domain'], + $locale['path'], +); +// we can pass $l10n to smarty here too +$smarty_ml = new \CoreLibs\Template\SmartyExtend($l10n, $locale); +$smarty_ml->template_dir = '../' . INCLUDES . TEMPLATES . CONTENT_PATH; + +$language = DEFAULT_LOCALE; +$encoding = DEFAULT_ENCODING; +$domain = 'messages.' . $encoding; +$locale_path = BASE . INCLUDES . LANG; +// alternative l10n test +$loader = new PhpMyAdmin\MoTranslator\Loader(); +// Set locale +$loader->setlocale($language); +// Set default text domain +$loader->textdomain($domain); +// Set path where to look for a domain +$loader->bindtextdomain($domain, $locale_path); +// Get translator +$translator = $loader->getTranslator(); +// some translation +$DATA['translation_test'] = $translator->gettext('Original'); + +$DATA['DEFAULT_ENCODING'] = DEFAULT_ENCODING; +$DATA['HTML_TITLE'] = 'Smarty Test Template'; +$DATA['BASE'] = BASE; +$DATA['translate'] = 'Original'; +$DATA['translate_out'] = $l10n->__($DATA['translate']); +$DATA['test'] = 'foo'; +$DATA['foo'] = 'bar'; +$DATA['replace'] = 'Replaced'; +$DATA['loop_start'] = 5; +$DATA['drop_down_test'] = [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'foobar' => 'Foo Bar', +]; +$DATA['drop_down_test_selected'] = 'bar'; +$DATA['drop_down_test_nested'] = [ + '' => '選択してください', + '4/25(木)' => [ + '4/25(木) 11:00-11:50' => '4/25(木) 11:00-11:50', + '4/25(木) 12:20-13:00' => '4/25(木) 12:20-13:00' + ], + '4/26(金)' => [ + '4/26(金) 11:00-11:50' => '4/26(金) 11:00-11:50', + '4/26(金) 12:20-13:00' => '4/26(金) 12:20-13:00' + ], + '4/27(土)' => [ + '4/27(土) 11:00-11:50' => '4/27(土) 11:00-11:50', + '4/27(土) 12:20-13:00' => '4/27(土) 12:20-13:00' + ], +]; +$DATA['drop_down_test_nested_selected'] = ''; +$DATA['radio_test'] = [ + '0' => 'On', + '1' => 'Off', + '-1' => 'Undefined' +]; +$DATA['radio_test_selected'] = -1; +$DATA['checkbox_test'] = [ + '0' => 'On', + '1' => 'Off', + '-1' => 'Undefined' +]; +$DATA['checkbox_test_pos'] = [ + '0' => 'A', + '1' => 'B' +]; +$DATA['checkbox_test_selected'] = ''; + +// smarty part +$smarty_ml->setCompileDir(BASE . TEMPLATES_C); +$smarty_ml->setCacheDir(BASE . CACHE); + +foreach ($DATA as $key => $value) { + $smarty_ml->assign($key, $value); +} +// content (text) is array in templates +$smarty_ml->display('test.tpl'); + +// __END__ diff --git a/test/src/overlib.js b/test/src/overlib.js new file mode 100644 index 0000000..ebbc05d --- /dev/null +++ b/test/src/overlib.js @@ -0,0 +1,1491 @@ +//\///// +//\ overLIB 4.21 - You may not remove or change this notice. +//\ Copyright Erik Bosrup 1998-2004. All rights reserved. +//\ +//\ Contributors are listed on the homepage. +//\ This file might be old, always check for the latest version at: +//\ http://www.bosrup.com/web/overlib/ +//\ +//\ Please read the license agreement (available through the link above) +//\ before using overLIB. Direct any licensing questions to erik@bosrup.com. +//\ +//\ Do not sell this as your own work or remove this copyright notice. +//\ For full details on copying or changing this script please read the +//\ license agreement at the link above. Please give credit on sites that +//\ use overLIB and submit changes of the script so other people can use +//\ them as well. +// $Revision: 3159 $ $Date: 2010-09-02 11:58:10 +0900 (Thu, 02 Sep 2010) $ +//\///// +//\mini + +//////// +// PRE-INIT +// Ignore these lines, configuration is below. +//////// +var olLoaded = 0;var pmStart = 10000000; var pmUpper = 10001000; var pmCount = pmStart+1; var pmt=''; var pms = new Array(); var olInfo = new Info('4.21', 1); +var FREPLACE = 0; var FBEFORE = 1; var FAFTER = 2; var FALTERNATE = 3; var FCHAIN=4; +var olHideForm=0; // parameter for hiding SELECT and ActiveX elements in IE5.5+ +var olHautoFlag = 0; // flags for over-riding VAUTO and HAUTO if corresponding +var olVautoFlag = 0; // positioning commands are used on the command line +var hookPts = new Array(), postParse = new Array(), cmdLine = new Array(), runTime = new Array(); +// for plugins +registerCommands('donothing,inarray,caparray,sticky,background,noclose,caption,left,right,center,offsetx,offsety,fgcolor,bgcolor,textcolor,capcolor,closecolor,width,border,cellpad,status,autostatus,autostatuscap,height,closetext,snapx,snapy,fixx,fixy,relx,rely,fgbackground,bgbackground,padx,pady,fullhtml,above,below,capicon,textfont,captionfont,closefont,textsize,captionsize,closesize,timeout,function,delay,hauto,vauto,closeclick,wrap,followmouse,mouseoff,closetitle,cssoff,compatmode,cssclass,fgclass,bgclass,textfontclass,captionfontclass,closefontclass'); + +//////// +// DEFAULT CONFIGURATION +// Settings you want everywhere are set here. All of this can also be +// changed on your html page or through an overLIB call. +//////// +if (typeof ol_fgcolor=='undefined') var ol_fgcolor="#CCCCFF"; +if (typeof ol_bgcolor=='undefined') var ol_bgcolor="#333399"; +if (typeof ol_textcolor=='undefined') var ol_textcolor="#000000"; +if (typeof ol_capcolor=='undefined') var ol_capcolor="#FFFFFF"; +if (typeof ol_closecolor=='undefined') var ol_closecolor="#9999FF"; +if (typeof ol_textfont=='undefined') var ol_textfont="Verdana,Arial,Helvetica"; +if (typeof ol_captionfont=='undefined') var ol_captionfont="Verdana,Arial,Helvetica"; +if (typeof ol_closefont=='undefined') var ol_closefont="Verdana,Arial,Helvetica"; +if (typeof ol_textsize=='undefined') var ol_textsize="1"; +if (typeof ol_captionsize=='undefined') var ol_captionsize="1"; +if (typeof ol_closesize=='undefined') var ol_closesize="1"; +if (typeof ol_width=='undefined') var ol_width="200"; +if (typeof ol_border=='undefined') var ol_border="1"; +if (typeof ol_cellpad=='undefined') var ol_cellpad=2; +if (typeof ol_offsetx=='undefined') var ol_offsetx=10; +if (typeof ol_offsety=='undefined') var ol_offsety=10; +if (typeof ol_text=='undefined') var ol_text="Default Text"; +if (typeof ol_cap=='undefined') var ol_cap=""; +if (typeof ol_sticky=='undefined') var ol_sticky=0; +if (typeof ol_background=='undefined') var ol_background=""; +if (typeof ol_close=='undefined') var ol_close="Close"; +if (typeof ol_hpos=='undefined') var ol_hpos=RIGHT; +if (typeof ol_status=='undefined') var ol_status=""; +if (typeof ol_autostatus=='undefined') var ol_autostatus=0; +if (typeof ol_height=='undefined') var ol_height=-1; +if (typeof ol_snapx=='undefined') var ol_snapx=0; +if (typeof ol_snapy=='undefined') var ol_snapy=0; +if (typeof ol_fixx=='undefined') var ol_fixx=-1; +if (typeof ol_fixy=='undefined') var ol_fixy=-1; +if (typeof ol_relx=='undefined') var ol_relx=null; +if (typeof ol_rely=='undefined') var ol_rely=null; +if (typeof ol_fgbackground=='undefined') var ol_fgbackground=""; +if (typeof ol_bgbackground=='undefined') var ol_bgbackground=""; +if (typeof ol_padxl=='undefined') var ol_padxl=1; +if (typeof ol_padxr=='undefined') var ol_padxr=1; +if (typeof ol_padyt=='undefined') var ol_padyt=1; +if (typeof ol_padyb=='undefined') var ol_padyb=1; +if (typeof ol_fullhtml=='undefined') var ol_fullhtml=0; +if (typeof ol_vpos=='undefined') var ol_vpos=BELOW; +if (typeof ol_aboveheight=='undefined') var ol_aboveheight=0; +if (typeof ol_capicon=='undefined') var ol_capicon=""; +if (typeof ol_frame=='undefined') var ol_frame=self; +if (typeof ol_timeout=='undefined') var ol_timeout=0; +if (typeof ol_function=='undefined') var ol_function=null; +if (typeof ol_delay=='undefined') var ol_delay=0; +if (typeof ol_hauto=='undefined') var ol_hauto=0; +if (typeof ol_vauto=='undefined') var ol_vauto=0; +if (typeof ol_closeclick=='undefined') var ol_closeclick=0; +if (typeof ol_wrap=='undefined') var ol_wrap=0; +if (typeof ol_followmouse=='undefined') var ol_followmouse=1; +if (typeof ol_mouseoff=='undefined') var ol_mouseoff=0; +if (typeof ol_closetitle=='undefined') var ol_closetitle='Close'; +if (typeof ol_compatmode=='undefined') var ol_compatmode=0; +if (typeof ol_css=='undefined') var ol_css=CSSOFF; +if (typeof ol_fgclass=='undefined') var ol_fgclass=""; +if (typeof ol_bgclass=='undefined') var ol_bgclass=""; +if (typeof ol_textfontclass=='undefined') var ol_textfontclass=""; +if (typeof ol_captionfontclass=='undefined') var ol_captionfontclass=""; +if (typeof ol_closefontclass=='undefined') var ol_closefontclass=""; + +//////// +// ARRAY CONFIGURATION +//////// + +// You can use these arrays to store popup text here instead of in the html. +if (typeof ol_texts=='undefined') var ol_texts = new Array("Text 0", "Text 1"); +if (typeof ol_caps=='undefined') var ol_caps = new Array("Caption 0", "Caption 1"); + +//////// +// END OF CONFIGURATION +// Don't change anything below this line, all configuration is above. +//////// + + + + + +//////// +// INIT +//////// +// Runtime variables init. Don't change for config! +var o3_text=""; +var o3_cap=""; +var o3_sticky=0; +var o3_background=""; +var o3_close="Close"; +var o3_hpos=RIGHT; +var o3_offsetx=2; +var o3_offsety=2; +var o3_fgcolor=""; +var o3_bgcolor=""; +var o3_textcolor=""; +var o3_capcolor=""; +var o3_closecolor=""; +var o3_width=100; +var o3_border=1; +var o3_cellpad=2; +var o3_status=""; +var o3_autostatus=0; +var o3_height=-1; +var o3_snapx=0; +var o3_snapy=0; +var o3_fixx=-1; +var o3_fixy=-1; +var o3_relx=null; +var o3_rely=null; +var o3_fgbackground=""; +var o3_bgbackground=""; +var o3_padxl=0; +var o3_padxr=0; +var o3_padyt=0; +var o3_padyb=0; +var o3_fullhtml=0; +var o3_vpos=BELOW; +var o3_aboveheight=0; +var o3_capicon=""; +var o3_textfont="Verdana,Arial,Helvetica"; +var o3_captionfont="Verdana,Arial,Helvetica"; +var o3_closefont="Verdana,Arial,Helvetica"; +var o3_textsize="1"; +var o3_captionsize="1"; +var o3_closesize="1"; +var o3_frame=self; +var o3_timeout=0; +var o3_timerid=0; +var o3_allowmove=0; +var o3_function=null; +var o3_delay=0; +var o3_delayid=0; +var o3_hauto=0; +var o3_vauto=0; +var o3_closeclick=0; +var o3_wrap=0; +var o3_followmouse=1; +var o3_mouseoff=0; +var o3_closetitle=''; +var o3_compatmode=0; +var o3_css=CSSOFF; +var o3_fgclass=""; +var o3_bgclass=""; +var o3_textfontclass=""; +var o3_captionfontclass=""; +var o3_closefontclass=""; + +// Display state variables +var o3_x = 0; +var o3_y = 0; +var o3_showingsticky = 0; +var o3_removecounter = 0; + +// Our layer +var over = null; +var fnRef, hoveringSwitch = false; +var olHideDelay; + +// Decide browser version +var isMac = (navigator.userAgent.indexOf("Mac") != -1); +var olOp = (navigator.userAgent.toLowerCase().indexOf('opera') > -1 && document.createTextNode); // Opera 7 +var olNs4 = (navigator.appName=='Netscape' && parseInt(navigator.appVersion) == 4); +var olNs6 = (document.getElementById) ? true : false; +var olKq = (olNs6 && /konqueror/i.test(navigator.userAgent)); +var olIe4 = (document.all) ? true : false; +var olIe5 = false; +var olIe55 = false; // Added additional variable to identify IE5.5+ +var docRoot = 'document.body'; + +// Resize fix for NS4.x to keep track of layer +if (olNs4) { + var oW = window.innerWidth; + var oH = window.innerHeight; + window.onresize = function() { if (oW != window.innerWidth || oH != window.innerHeight) location.reload(); } +} + +// Microsoft Stupidity Check(tm). +if (olIe4) { + var agent = navigator.userAgent; + if (/MSIE/.test(agent)) { + var versNum = parseFloat(agent.match(/MSIE[ ](\d\.\d+)\.*/i)[1]); + if (versNum >= 5){ + olIe5=true; + olIe55=(versNum>=5.5&&!olOp) ? true : false; + if (olNs6) olNs6=false; + } + } + if (olNs6) olIe4 = false; +} + +// Check for compatability mode. +if (document.compatMode && document.compatMode == 'CSS1Compat') { + docRoot= ((olIe4 && !olOp) ? 'document.documentElement' : docRoot); +} + +// Add window onload handlers to indicate when all modules have been loaded +// For Netscape 6+ and Mozilla, uses addEventListener method on the window object +// For IE it uses the attachEvent method of the window object and for Netscape 4.x +// it sets the window.onload handler to the OLonload_handler function for Bubbling +if(window.addEventListener) window.addEventListener("load",OLonLoad_handler,false); +else if (window.attachEvent) window.attachEvent("onload",OLonLoad_handler); + +var capExtent; + +//////// +// PUBLIC FUNCTIONS +//////// + +// overlib(arg0,...,argN) +// Loads parameters into global runtime variables. +function overlib() { + if (!olLoaded || isExclusive(overlib.arguments)) return true; + if (olCheckMouseCapture) olMouseCapture(); + if (over) { + over = (typeof over.id != 'string') ? o3_frame.document.all['overDiv'] : over; + cClick(); + } + + // Load defaults to runtime. + olHideDelay=0; + o3_text=ol_text; + o3_cap=ol_cap; + o3_sticky=ol_sticky; + o3_background=ol_background; + o3_close=ol_close; + o3_hpos=ol_hpos; + o3_offsetx=ol_offsetx; + o3_offsety=ol_offsety; + o3_fgcolor=ol_fgcolor; + o3_bgcolor=ol_bgcolor; + o3_textcolor=ol_textcolor; + o3_capcolor=ol_capcolor; + o3_closecolor=ol_closecolor; + o3_width=ol_width; + o3_border=ol_border; + o3_cellpad=ol_cellpad; + o3_status=ol_status; + o3_autostatus=ol_autostatus; + o3_height=ol_height; + o3_snapx=ol_snapx; + o3_snapy=ol_snapy; + o3_fixx=ol_fixx; + o3_fixy=ol_fixy; + o3_relx=ol_relx; + o3_rely=ol_rely; + o3_fgbackground=ol_fgbackground; + o3_bgbackground=ol_bgbackground; + o3_padxl=ol_padxl; + o3_padxr=ol_padxr; + o3_padyt=ol_padyt; + o3_padyb=ol_padyb; + o3_fullhtml=ol_fullhtml; + o3_vpos=ol_vpos; + o3_aboveheight=ol_aboveheight; + o3_capicon=ol_capicon; + o3_textfont=ol_textfont; + o3_captionfont=ol_captionfont; + o3_closefont=ol_closefont; + o3_textsize=ol_textsize; + o3_captionsize=ol_captionsize; + o3_closesize=ol_closesize; + o3_timeout=ol_timeout; + o3_function=ol_function; + o3_delay=ol_delay; + o3_hauto=ol_hauto; + o3_vauto=ol_vauto; + o3_closeclick=ol_closeclick; + o3_wrap=ol_wrap; + o3_followmouse=ol_followmouse; + o3_mouseoff=ol_mouseoff; + o3_closetitle=ol_closetitle; + o3_css=ol_css; + o3_compatmode=ol_compatmode; + o3_fgclass=ol_fgclass; + o3_bgclass=ol_bgclass; + o3_textfontclass=ol_textfontclass; + o3_captionfontclass=ol_captionfontclass; + o3_closefontclass=ol_closefontclass; + + setRunTimeVariables(); + + fnRef = ''; + + // Special for frame support, over must be reset... + o3_frame = ol_frame; + + if(!(over=createDivContainer())) return false; + + parseTokens('o3_', overlib.arguments); + if (!postParseChecks()) return false; + + if (o3_delay == 0) { + return runHook("olMain", FREPLACE); + } else { + o3_delayid = setTimeout("runHook('olMain', FREPLACE)", o3_delay); + return false; + } +} + +// Clears popups if appropriate +function nd(time) { + if (olLoaded && !isExclusive()) { + hideDelay(time); // delay popup close if time specified + + if (o3_removecounter >= 1) { o3_showingsticky = 0 }; + + if (o3_showingsticky == 0) { + o3_allowmove = 0; + if (over != null && o3_timerid == 0) runHook("hideObject", FREPLACE, over); + } else { + o3_removecounter++; + } + } + + return true; +} + +// The Close onMouseOver function for stickies +function cClick() { + if (olLoaded) { + runHook("hideObject", FREPLACE, over); + o3_showingsticky = 0; + } + return false; +} + +// Method for setting page specific defaults. +function overlib_pagedefaults() { + parseTokens('ol_', overlib_pagedefaults.arguments); +} + + +//////// +// OVERLIB MAIN FUNCTION +//////// + +// This function decides what it is we want to display and how we want it done. +function olMain() { + var layerhtml, styleType; + runHook("olMain", FBEFORE); + + if (o3_background!="" || o3_fullhtml) { + // Use background instead of box. + layerhtml = runHook('ol_content_background', FALTERNATE, o3_css, o3_text, o3_background, o3_fullhtml); + } else { + // They want a popup box. + styleType = (pms[o3_css-1-pmStart] == "cssoff" || pms[o3_css-1-pmStart] == "cssclass"); + + // Prepare popup background + if (o3_fgbackground != "") o3_fgbackground = "background=\""+o3_fgbackground+"\""; + if (o3_bgbackground != "") o3_bgbackground = (styleType ? "background=\""+o3_bgbackground+"\"" : o3_bgbackground); + + // Prepare popup colors + if (o3_fgcolor != "") o3_fgcolor = (styleType ? "bgcolor=\""+o3_fgcolor+"\"" : o3_fgcolor); + if (o3_bgcolor != "") o3_bgcolor = (styleType ? "bgcolor=\""+o3_bgcolor+"\"" : o3_bgcolor); + + // Prepare popup height + if (o3_height > 0) o3_height = (styleType ? "height=\""+o3_height+"\"" : o3_height); + else o3_height = ""; + + // Decide which kinda box. + if (o3_cap=="") { + // Plain + layerhtml = runHook('ol_content_simple', FALTERNATE, o3_css, o3_text); + } else { + // With caption + if (o3_sticky) { + // Show close text + layerhtml = runHook('ol_content_caption', FALTERNATE, o3_css, o3_text, o3_cap, o3_close); + } else { + // No close text + layerhtml = runHook('ol_content_caption', FALTERNATE, o3_css, o3_text, o3_cap, ""); + } + } + } + + // We want it to stick! + if (o3_sticky) { + if (o3_timerid > 0) { + clearTimeout(o3_timerid); + o3_timerid = 0; + } + o3_showingsticky = 1; + o3_removecounter = 0; + } + + // Created a separate routine to generate the popup to make it easier + // to implement a plugin capability + if (!runHook("createPopup", FREPLACE, layerhtml)) return false; + + // Prepare status bar + if (o3_autostatus > 0) { + o3_status = o3_text; + if (o3_autostatus > 1) o3_status = o3_cap; + } + + // When placing the layer the first time, even stickies may be moved. + o3_allowmove = 0; + + // Initiate a timer for timeout + if (o3_timeout > 0) { + if (o3_timerid > 0) clearTimeout(o3_timerid); + o3_timerid = setTimeout("cClick()", o3_timeout); + } + + // Show layer + runHook("disp", FREPLACE, o3_status); + runHook("olMain", FAFTER); + + return (olOp && event && event.type == 'mouseover' && !o3_status) ? '' : (o3_status != ''); +} + +//////// +// LAYER GENERATION FUNCTIONS +//////// +// These functions just handle popup content with tags that should adhere to the W3C standards specification. + +// Makes simple table without caption +function ol_content_simple(text) { + var cpIsMultiple = /,/.test(o3_cellpad); + var txt = '
' : ((!olNs4&&cpIsMultiple) ? ' style="'+setCellPadStr(o3_cellpad)+'">' : '>'))+(o3_textfontclass ? '' : wrapStr(0,o3_textsize,'text'))+text+(o3_textfontclass ? '' : wrapStr(1,o3_textsize))+'
'; + + set_background(""); + return txt; +} + +// Makes table with caption and optional close link +function ol_content_caption(text,title,close) { + var nameId, txt, cpIsMultiple = /,/.test(o3_cellpad); + var closing, closeevent; + + closing = ""; + closeevent = "onmouseover"; + if (o3_closeclick == 1) closeevent = (o3_closetitle ? "title='" + o3_closetitle +"'" : "") + " onclick"; + if (o3_capicon != "") { + nameId = ' hspace = \"5\"'+' align = \"middle\" alt = \"\"'; + if (typeof o3_dragimg != 'undefined' && o3_dragimg) nameId =' hspace=\"5\"'+' name=\"'+o3_dragimg+'\" id=\"'+o3_dragimg+'\" align=\"middle\" alt=\"Drag Enabled\" title=\"Drag Enabled\"'; + o3_capicon = ''; + } + + if (close != "") + closing = ''+(o3_closefontclass ? '' : wrapStr(0,o3_closesize,'close'))+close+(o3_closefontclass ? '' : wrapStr(1,o3_closesize,'close'))+''; + txt = '
' : '>')+(o3_captionfontclass ? '' : ''+wrapStr(0,o3_captionsize,'caption'))+o3_capicon+title+(o3_captionfontclass ? '' : wrapStr(1,o3_captionsize)+'')+''+closing+'
' :((!olNs4&&cpIsMultiple) ? ' style="'+setCellPadStr(o3_cellpad)+'">' : '>'))+(o3_textfontclass ? '' : wrapStr(0,o3_textsize,'text'))+text+(o3_textfontclass ? '' : wrapStr(1,o3_textsize)) + '
'; + + set_background(""); + return txt; +} + +// Sets the background picture,padding and lots more. :) +function ol_content_background(text,picture,hasfullhtml) { + if (hasfullhtml) { + txt=text; + } else { + txt='
'+(o3_textfontclass ? '' : wrapStr(0,o3_textsize,'text'))+text+(o3_textfontclass ? '' : wrapStr(1,o3_textsize))+'
'; + } + + set_background(picture); + return txt; +} + +// Loads a picture into the div. +function set_background(pic) { + if (pic == "") { + if (olNs4) { + over.background.src = null; + } else if (over.style) { + over.style.backgroundImage = "none"; + } + } else { + if (olNs4) { + over.background.src = pic; + } else if (over.style) { + over.style.width=o3_width + 'px'; + over.style.backgroundImage = "url("+pic+")"; + } + } +} + +//////// +// HANDLING FUNCTIONS +//////// +var olShowId=-1; + +// Displays the popup +function disp(statustext) { + runHook("disp", FBEFORE); + + if (o3_allowmove == 0) { + runHook("placeLayer", FREPLACE); + (olNs6&&olShowId<0) ? olShowId=setTimeout("runHook('showObject', FREPLACE, over)", 1) : runHook("showObject", FREPLACE, over); + o3_allowmove = (o3_sticky || o3_followmouse==0) ? 0 : 1; + } + + runHook("disp", FAFTER); + + if (statustext != "") self.status = statustext; +} + +// Creates the actual popup structure +function createPopup(lyrContent){ + runHook("createPopup", FBEFORE); + + if (o3_wrap) { + var wd,ww,theObj = (olNs4 ? over : over.style); + theObj.top = theObj.left = ((olIe4&&!olOp) ? 0 : -10000) + (!olNs4 ? 'px' : 0); + layerWrite(lyrContent); + wd = (olNs4 ? over.clip.width : over.offsetWidth); + if (wd > (ww=windowWidth())) { + lyrContent=lyrContent.replace(/\ /g, ' '); + o3_width=ww; + o3_wrap=0; + } + } + + layerWrite(lyrContent); + + // Have to set o3_width for placeLayer() routine if o3_wrap is turned on + if (o3_wrap) o3_width=(olNs4 ? over.clip.width : over.offsetWidth); + + runHook("createPopup", FAFTER, lyrContent); + + return true; +} + +// Decides where we want the popup. +function placeLayer() { + var placeX, placeY, widthFix = 0; + + // HORIZONTAL PLACEMENT, re-arranged to work in Safari + if (o3_frame.innerWidth) widthFix=18; + iwidth = windowWidth(); + + // Horizontal scroll offset + winoffset=(olIe4) ? eval('o3_frame.'+docRoot+'.scrollLeft') : o3_frame.pageXOffset; + + placeX = runHook('horizontalPlacement',FCHAIN,iwidth,winoffset,widthFix); + + // VERTICAL PLACEMENT, re-arranged to work in Safari + if (o3_frame.innerHeight) { + iheight=o3_frame.innerHeight; + } else if (eval('o3_frame.'+docRoot)&&eval("typeof o3_frame."+docRoot+".clientHeight=='number'")&&eval('o3_frame.'+docRoot+'.clientHeight')) { + iheight=eval('o3_frame.'+docRoot+'.clientHeight'); + } + + // Vertical scroll offset + scrolloffset=(olIe4) ? eval('o3_frame.'+docRoot+'.scrollTop') : o3_frame.pageYOffset; + placeY = runHook('verticalPlacement',FCHAIN,iheight,scrolloffset); + + // Actually move the object. + repositionTo(over, placeX, placeY); +} + +// Moves the layer +function olMouseMove(e) { + var e = (e) ? e : event; + + if (e.pageX) { + o3_x = e.pageX; + o3_y = e.pageY; + } else if (e.clientX) { + o3_x = eval('e.clientX+o3_frame.'+docRoot+'.scrollLeft'); + o3_y = eval('e.clientY+o3_frame.'+docRoot+'.scrollTop'); + } + + if (o3_allowmove == 1) runHook("placeLayer", FREPLACE); + + // MouseOut handler + if (hoveringSwitch && !olNs4 && runHook("cursorOff", FREPLACE)) { + (olHideDelay ? hideDelay(olHideDelay) : cClick()); + hoveringSwitch = !hoveringSwitch; + } +} + +// Fake function for 3.0 users. +function no_overlib() { return ver3fix; } + +// Capture the mouse and chain other scripts. +function olMouseCapture() { + capExtent = document; + var fN, str = '', l, k, f, wMv, sS, mseHandler = olMouseMove; + var re = /function[ ]*(\w*)\(/; + + wMv = (!olIe4 && window.onmousemove); + if (document.onmousemove || wMv) { + if (wMv) capExtent = window; + f = capExtent.onmousemove.toString(); + fN = f.match(re); + if (fN == null) { + str = f+'(e); '; + } else if (fN[1] == 'anonymous' || fN[1] == 'olMouseMove' || (wMv && fN[1] == 'onmousemove')) { + if (!olOp && wMv) { + l = f.indexOf('{')+1; + k = f.lastIndexOf('}'); + sS = f.substring(l,k); + if ((l = sS.indexOf('(')) != -1) { + sS = sS.substring(0,l).replace(/^\s+/,'').replace(/\s+$/,''); + if (eval("typeof " + sS + " == 'undefined'")) window.onmousemove = null; + else str = sS + '(e);'; + } + } + if (!str) { + olCheckMouseCapture = false; + return; + } + } else { + if (fN[1]) str = fN[1]+'(e); '; + else { + l = f.indexOf('{')+1; + k = f.lastIndexOf('}'); + str = f.substring(l,k) + '\n'; + } + } + str += 'olMouseMove(e); '; + mseHandler = new Function('e', str); + } + + capExtent.onmousemove = mseHandler; + if (olNs4) capExtent.captureEvents(Event.MOUSEMOVE); +} + +//////// +// PARSING FUNCTIONS +//////// + +// Does the actual command parsing. +function parseTokens(pf, ar) { + // What the next argument is expected to be. + var v, i, mode=-1, par = (pf != 'ol_'); + var fnMark = (par && !ar.length ? 1 : 0); + + for (i = 0; i < ar.length; i++) { + if (mode < 0) { + // Arg is maintext,unless its a number between pmStart and pmUpper + // then its a command. + if (typeof ar[i] == 'number' && ar[i] > pmStart && ar[i] < pmUpper) { + fnMark = (par ? 1 : 0); + i--; // backup one so that the next block can parse it + } else { + switch(pf) { + case 'ol_': + ol_text = ar[i].toString(); + break; + default: + o3_text=ar[i].toString(); + } + } + mode = 0; + } else { + // Note: NS4 doesn't like switch cases with vars. + if (ar[i] >= pmCount || ar[i]==DONOTHING) { continue; } + if (ar[i]==INARRAY) { fnMark = 0; eval(pf+'text=ol_texts['+ar[++i]+'].toString()'); continue; } + if (ar[i]==CAPARRAY) { eval(pf+'cap=ol_caps['+ar[++i]+'].toString()'); continue; } + if (ar[i]==STICKY) { if (pf!='ol_') eval(pf+'sticky=1'); continue; } + if (ar[i]==BACKGROUND) { eval(pf+'background="'+ar[++i]+'"'); continue; } + if (ar[i]==NOCLOSE) { if (pf!='ol_') opt_NOCLOSE(); continue; } + if (ar[i]==CAPTION) { eval(pf+"cap='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==CENTER || ar[i]==LEFT || ar[i]==RIGHT) { eval(pf+'hpos='+ar[i]); if(pf!='ol_') olHautoFlag=1; continue; } + if (ar[i]==OFFSETX) { eval(pf+'offsetx='+ar[++i]); continue; } + if (ar[i]==OFFSETY) { eval(pf+'offsety='+ar[++i]); continue; } + if (ar[i]==FGCOLOR) { eval(pf+'fgcolor="'+ar[++i]+'"'); continue; } + if (ar[i]==BGCOLOR) { eval(pf+'bgcolor="'+ar[++i]+'"'); continue; } + if (ar[i]==TEXTCOLOR) { eval(pf+'textcolor="'+ar[++i]+'"'); continue; } + if (ar[i]==CAPCOLOR) { eval(pf+'capcolor="'+ar[++i]+'"'); continue; } + if (ar[i]==CLOSECOLOR) { eval(pf+'closecolor="'+ar[++i]+'"'); continue; } + if (ar[i]==WIDTH) { eval(pf+'width='+ar[++i]); continue; } + if (ar[i]==BORDER) { eval(pf+'border='+ar[++i]); continue; } + if (ar[i]==CELLPAD) { i=opt_MULTIPLEARGS(++i,ar,(pf+'cellpad')); continue; } + if (ar[i]==STATUS) { eval(pf+"status='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==AUTOSTATUS) { eval(pf +'autostatus=('+pf+'autostatus == 1) ? 0 : 1'); continue; } + if (ar[i]==AUTOSTATUSCAP) { eval(pf +'autostatus=('+pf+'autostatus == 2) ? 0 : 2'); continue; } + if (ar[i]==HEIGHT) { eval(pf+'height='+pf+'aboveheight='+ar[++i]); continue; } // Same param again. + if (ar[i]==CLOSETEXT) { eval(pf+"close='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==SNAPX) { eval(pf+'snapx='+ar[++i]); continue; } + if (ar[i]==SNAPY) { eval(pf+'snapy='+ar[++i]); continue; } + if (ar[i]==FIXX) { eval(pf+'fixx='+ar[++i]); continue; } + if (ar[i]==FIXY) { eval(pf+'fixy='+ar[++i]); continue; } + if (ar[i]==RELX) { eval(pf+'relx='+ar[++i]); continue; } + if (ar[i]==RELY) { eval(pf+'rely='+ar[++i]); continue; } + if (ar[i]==FGBACKGROUND) { eval(pf+'fgbackground="'+ar[++i]+'"'); continue; } + if (ar[i]==BGBACKGROUND) { eval(pf+'bgbackground="'+ar[++i]+'"'); continue; } + if (ar[i]==PADX) { eval(pf+'padxl='+ar[++i]); eval(pf+'padxr='+ar[++i]); continue; } + if (ar[i]==PADY) { eval(pf+'padyt='+ar[++i]); eval(pf+'padyb='+ar[++i]); continue; } + if (ar[i]==FULLHTML) { if (pf!='ol_') eval(pf+'fullhtml=1'); continue; } + if (ar[i]==BELOW || ar[i]==ABOVE) { eval(pf+'vpos='+ar[i]); if (pf!='ol_') olVautoFlag=1; continue; } + if (ar[i]==CAPICON) { eval(pf+'capicon="'+ar[++i]+'"'); continue; } + if (ar[i]==TEXTFONT) { eval(pf+"textfont='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==CAPTIONFONT) { eval(pf+"captionfont='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==CLOSEFONT) { eval(pf+"closefont='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==TEXTSIZE) { eval(pf+'textsize="'+ar[++i]+'"'); continue; } + if (ar[i]==CAPTIONSIZE) { eval(pf+'captionsize="'+ar[++i]+'"'); continue; } + if (ar[i]==CLOSESIZE) { eval(pf+'closesize="'+ar[++i]+'"'); continue; } + if (ar[i]==TIMEOUT) { eval(pf+'timeout='+ar[++i]); continue; } + if (ar[i]==FUNCTION) { if (pf=='ol_') { if (typeof ar[i+1]!='number') { v=ar[++i]; ol_function=(typeof v=='function' ? v : null); }} else {fnMark = 0; v = null; if (typeof ar[i+1]!='number') v = ar[++i]; opt_FUNCTION(v); } continue; } + if (ar[i]==DELAY) { eval(pf+'delay='+ar[++i]); continue; } + if (ar[i]==HAUTO) { eval(pf+'hauto=('+pf+'hauto == 0) ? 1 : 0'); continue; } + if (ar[i]==VAUTO) { eval(pf+'vauto=('+pf+'vauto == 0) ? 1 : 0'); continue; } + if (ar[i]==CLOSECLICK) { eval(pf +'closeclick=('+pf+'closeclick == 0) ? 1 : 0'); continue; } + if (ar[i]==WRAP) { eval(pf +'wrap=('+pf+'wrap == 0) ? 1 : 0'); continue; } + if (ar[i]==FOLLOWMOUSE) { eval(pf +'followmouse=('+pf+'followmouse == 1) ? 0 : 1'); continue; } + if (ar[i]==MOUSEOFF) { eval(pf +'mouseoff=('+pf+'mouseoff==0) ? 1 : 0'); v=ar[i+1]; if (pf != 'ol_' && eval(pf+'mouseoff') && typeof v == 'number' && (v < pmStart || v > pmUpper)) olHideDelay=ar[++i]; continue; } + if (ar[i]==CLOSETITLE) { eval(pf+"closetitle='"+escSglQuote(ar[++i])+"'"); continue; } + if (ar[i]==CSSOFF||ar[i]==CSSCLASS) { eval(pf+'css='+ar[i]); continue; } + if (ar[i]==COMPATMODE) { eval(pf+'compatmode=('+pf+'compatmode==0) ? 1 : 0'); continue; } + if (ar[i]==FGCLASS) { eval(pf+'fgclass="'+ar[++i]+'"'); continue; } + if (ar[i]==BGCLASS) { eval(pf+'bgclass="'+ar[++i]+'"'); continue; } + if (ar[i]==TEXTFONTCLASS) { eval(pf+'textfontclass="'+ar[++i]+'"'); continue; } + if (ar[i]==CAPTIONFONTCLASS) { eval(pf+'captionfontclass="'+ar[++i]+'"'); continue; } + if (ar[i]==CLOSEFONTCLASS) { eval(pf+'closefontclass="'+ar[++i]+'"'); continue; } + i = parseCmdLine(pf, i, ar); + } + } + + if (fnMark && o3_function) o3_text = o3_function(); + + if ((pf == 'o3_') && o3_wrap) { + o3_width = 0; + + var tReg=/<.*\n*>/ig; + if (!tReg.test(o3_text)) o3_text = o3_text.replace(/[ ]+/g, ' '); + if (!tReg.test(o3_cap))o3_cap = o3_cap.replace(/[ ]+/g, ' '); + } + if ((pf == 'o3_') && o3_sticky) { + if (!o3_close && (o3_frame != ol_frame)) o3_close = ol_close; + if (o3_mouseoff && (o3_frame == ol_frame)) opt_NOCLOSE(' '); + } +} + + +//////// +// LAYER FUNCTIONS +//////// + +// Writes to a layer +function layerWrite(txt) { + txt += "\n"; + if (olNs4) { + var lyr = o3_frame.document.layers['overDiv'].document + lyr.write(txt) + lyr.close() + } else if (typeof over.innerHTML != 'undefined') { + if (olIe5 && isMac) over.innerHTML = ''; + over.innerHTML = txt; + } else { + range = o3_frame.document.createRange(); + range.setStartAfter(over); + domfrag = range.createContextualFragment(txt); + + while (over.hasChildNodes()) { + over.removeChild(over.lastChild); + } + + over.appendChild(domfrag); + } +} + +// Make an object visible +function showObject(obj) { + runHook("showObject", FBEFORE); + + var theObj=(olNs4 ? obj : obj.style); + theObj.visibility = 'visible'; + + runHook("showObject", FAFTER); +} + +// Hides an object +function hideObject(obj) { + runHook("hideObject", FBEFORE); + + var theObj=(olNs4 ? obj : obj.style); + if (olNs6 && olShowId>0) { clearTimeout(olShowId); olShowId=0; } + theObj.visibility = 'hidden'; + theObj.top = theObj.left = ((olIe4&&!olOp) ? 0 : -10000) + (!olNs4 ? 'px' : 0); + + if (o3_timerid > 0) clearTimeout(o3_timerid); + if (o3_delayid > 0) clearTimeout(o3_delayid); + + o3_timerid = 0; + o3_delayid = 0; + self.status = ""; + + if (obj.onmouseout||obj.onmouseover) { + if (olNs4) obj.releaseEvents(Event.MOUSEOUT || Event.MOUSEOVER); + obj.onmouseout = obj.onmouseover = null; + } + + runHook("hideObject", FAFTER); +} + +// Move a layer +function repositionTo(obj, xL, yL) { + var theObj=(olNs4 ? obj : obj.style); + theObj.left = xL + (!olNs4 ? 'px' : 0); + theObj.top = yL + (!olNs4 ? 'px' : 0); +} + +// Check position of cursor relative to overDiv DIVision; mouseOut function +function cursorOff() { + var left = parseInt(over.style.left); + var top = parseInt(over.style.top); + var right = left + (over.offsetWidth >= parseInt(o3_width) ? over.offsetWidth : parseInt(o3_width)); + var bottom = top + (over.offsetHeight >= o3_aboveheight ? over.offsetHeight : o3_aboveheight); + + if (o3_x < left || o3_x > right || o3_y < top || o3_y > bottom) return true; + + return false; +} + + +//////// +// COMMAND FUNCTIONS +//////// + +// Calls callme or the default function. +function opt_FUNCTION(callme) { + o3_text = (callme ? (typeof callme=='string' ? (/.+\(.*\)/.test(callme) ? eval(callme) : callme) : callme()) : (o3_function ? o3_function() : 'No Function')); + + return 0; +} + +// Handle hovering +function opt_NOCLOSE(unused) { + if (!unused) o3_close = ""; + + if (olNs4) { + over.captureEvents(Event.MOUSEOUT || Event.MOUSEOVER); + over.onmouseover = function () { if (o3_timerid > 0) { clearTimeout(o3_timerid); o3_timerid = 0; } } + over.onmouseout = function (e) { if (olHideDelay) hideDelay(olHideDelay); else cClick(e); } + } else { + over.onmouseover = function () {hoveringSwitch = true; if (o3_timerid > 0) { clearTimeout(o3_timerid); o3_timerid =0; } } + } + + return 0; +} + +// Function to scan command line arguments for multiples +function opt_MULTIPLEARGS(i, args, parameter) { + var k=i, re, pV, str=''; + + for(k=i; kpmStart) break; + str += args[k] + ','; + } + if (str) str = str.substring(0,--str.length); + + k--; // reduce by one so the for loop this is in works correctly + pV=(olNs4 && /cellpad/i.test(parameter)) ? str.split(',')[0] : str; + eval(parameter + '="' + pV + '"'); + + return k; +} + +// Remove   in texts when done. +function nbspCleanup() { + if (o3_wrap) { + o3_text = o3_text.replace(/\ /g, ' '); + o3_cap = o3_cap.replace(/\ /g, ' '); + } +} + +// Escape embedded single quotes in text strings +function escSglQuote(str) { + return str.toString().replace(/'/g,"\\'"); +} + +// Onload handler for window onload event +function OLonLoad_handler(e) { + var re = /\w+\(.*\)[;\s]+/g, olre = /overlib\(|nd\(|cClick\(/, fn, l, i; + + if(!olLoaded) olLoaded=1; + + // Remove it for Gecko based browsers + if(window.removeEventListener && e.eventPhase == 3) window.removeEventListener("load",OLonLoad_handler,false); + else if(window.detachEvent) { // and for IE and Opera 4.x but execute calls to overlib, nd, or cClick() + window.detachEvent("onload",OLonLoad_handler); + var fN = document.body.getAttribute('onload'); + if (fN) { + fN=fN.toString().match(re); + if (fN && fN.length) { + for (i=0; i' : '') : ''; + else { + fontStr='o3_'+whichString+'font'; + fontColor='o3_'+((whichString=='caption')? 'cap' : whichString)+'color'; + return (hasDims&&!olNs4) ? (isClose ? '' : '
') : ''; + } +} + +// Quotes Multi word font names; needed for CSS Standards adherence in font-family +function quoteMultiNameFonts(theFont) { + var v, pM=theFont.split(','); + for (var i=0; i 0) clearTimeout(o3_timerid); + + o3_timerid=setTimeout("cClick()",(o3_timeout=time)); + } +} + +// Was originally in the placeLayer() routine; separated out for future ease +function horizontalPlacement(browserWidth, horizontalScrollAmount, widthFix) { + var placeX, iwidth=browserWidth, winoffset=horizontalScrollAmount; + var parsedWidth = parseInt(o3_width); + + if (o3_fixx > -1 || o3_relx != null) { + // Fixed position + placeX=(o3_relx != null ? ( o3_relx < 0 ? winoffset +o3_relx+ iwidth - parsedWidth - widthFix : winoffset+o3_relx) : o3_fixx); + } else { + // If HAUTO, decide what to use. + if (o3_hauto == 1) { + if ((o3_x - winoffset) > (iwidth / 2)) { + o3_hpos = LEFT; + } else { + o3_hpos = RIGHT; + } + } + + // From mouse + if (o3_hpos == CENTER) { // Center + placeX = o3_x+o3_offsetx-(parsedWidth/2); + + if (placeX < winoffset) placeX = winoffset; + } + + if (o3_hpos == RIGHT) { // Right + placeX = o3_x+o3_offsetx; + + if ((placeX+parsedWidth) > (winoffset+iwidth - widthFix)) { + placeX = iwidth+winoffset - parsedWidth - widthFix; + if (placeX < 0) placeX = 0; + } + } + if (o3_hpos == LEFT) { // Left + placeX = o3_x-o3_offsetx-parsedWidth; + if (placeX < winoffset) placeX = winoffset; + } + + // Snapping! + if (o3_snapx > 1) { + var snapping = placeX % o3_snapx; + + if (o3_hpos == LEFT) { + placeX = placeX - (o3_snapx+snapping); + } else { + // CENTER and RIGHT + placeX = placeX+(o3_snapx - snapping); + } + + if (placeX < winoffset) placeX = winoffset; + } + } + + return placeX; +} + +// was originally in the placeLayer() routine; separated out for future ease +function verticalPlacement(browserHeight,verticalScrollAmount) { + var placeY, iheight=browserHeight, scrolloffset=verticalScrollAmount; + var parsedHeight=(o3_aboveheight ? parseInt(o3_aboveheight) : (olNs4 ? over.clip.height : over.offsetHeight)); + + if (o3_fixy > -1 || o3_rely != null) { + // Fixed position + placeY=(o3_rely != null ? (o3_rely < 0 ? scrolloffset+o3_rely+iheight - parsedHeight : scrolloffset+o3_rely) : o3_fixy); + } else { + // If VAUTO, decide what to use. + if (o3_vauto == 1) { + if ((o3_y - scrolloffset) > (iheight / 2) && o3_vpos == BELOW && (o3_y + parsedHeight + o3_offsety - (scrolloffset + iheight) > 0)) { + o3_vpos = ABOVE; + } else if (o3_vpos == ABOVE && (o3_y - (parsedHeight + o3_offsety) - scrolloffset < 0)) { + o3_vpos = BELOW; + } + } + + // From mouse + if (o3_vpos == ABOVE) { + if (o3_aboveheight == 0) o3_aboveheight = parsedHeight; + + placeY = o3_y - (o3_aboveheight+o3_offsety); + if (placeY < scrolloffset) placeY = scrolloffset; + } else { + // BELOW + placeY = o3_y+o3_offsety; + } + + // Snapping! + if (o3_snapy > 1) { + var snapping = placeY % o3_snapy; + + if (o3_aboveheight > 0 && o3_vpos == ABOVE) { + placeY = placeY - (o3_snapy+snapping); + } else { + placeY = placeY+(o3_snapy - snapping); + } + + if (placeY < scrolloffset) placeY = scrolloffset; + } + } + + return placeY; +} + +// checks positioning flags +function checkPositionFlags() { + if (olHautoFlag) olHautoFlag = o3_hauto=0; + if (olVautoFlag) olVautoFlag = o3_vauto=0; + return true; +} + +// get Browser window width +function windowWidth() { + var w; + if (o3_frame.innerWidth) w=o3_frame.innerWidth; + else if (eval('o3_frame.'+docRoot)&&eval("typeof o3_frame."+docRoot+".clientWidth=='number'")&&eval('o3_frame.'+docRoot+'.clientWidth')) + w=eval('o3_frame.'+docRoot+'.clientWidth'); + return w; +} + +// create the div container for popup content if it doesn't exist +function createDivContainer(id,frm,zValue) { + id = (id || 'overDiv'), frm = (frm || o3_frame), zValue = (zValue || 1000); + var objRef, divContainer = layerReference(id); + + if (divContainer == null) { + if (olNs4) { + divContainer = frm.document.layers[id] = new Layer(window.innerWidth, frm); + objRef = divContainer; + } else { + var body = (olIe4 ? frm.document.all.tags('BODY')[0] : frm.document.getElementsByTagName("BODY")[0]); + if (olIe4&&!document.getElementById) { + body.insertAdjacentHTML("beforeEnd",'
'); + divContainer=layerReference(id); + } else { + divContainer = frm.document.createElement("DIV"); + divContainer.id = id; + body.appendChild(divContainer); + } + objRef = divContainer.style; + } + + objRef.position = 'absolute'; + objRef.visibility = 'hidden'; + objRef.zIndex = zValue; + if (olIe4&&!olOp) objRef.left = objRef.top = '0px'; + else objRef.left = objRef.top = -10000 + (!olNs4 ? 'px' : 0); + } + + return divContainer; +} + +// get reference to a layer with ID=id +function layerReference(id) { + return (olNs4 ? o3_frame.document.layers[id] : (document.all ? o3_frame.document.all[id] : o3_frame.document.getElementById(id))); +} +//////// +// UTILITY FUNCTIONS +//////// + +// Checks if something is a function. +function isFunction(fnRef) { + var rtn = true; + + if (typeof fnRef == 'object') { + for (var i = 0; i < fnRef.length; i++) { + if (typeof fnRef[i]=='function') continue; + rtn = false; + break; + } + } else if (typeof fnRef != 'function') { + rtn = false; + } + + return rtn; +} + +// Converts an array into an argument string for use in eval. +function argToString(array, strtInd, argName) { + var jS = strtInd, aS = '', ar = array; + argName=(argName ? argName : 'ar'); + + if (ar.length > jS) { + for (var k = jS; k < ar.length; k++) aS += argName+'['+k+'], '; + aS = aS.substring(0, aS.length-2); + } + + return aS; +} + +// Places a hook in the correct position in a hook point. +function reOrder(hookPt, fnRef, order) { + var newPt = new Array(), match, i, j; + + if (!order || typeof order == 'undefined' || typeof order == 'number') return hookPt; + + if (typeof order=='function') { + if (typeof fnRef=='object') { + newPt = newPt.concat(fnRef); + } else { + newPt[newPt.length++]=fnRef; + } + + for (i = 0; i < hookPt.length; i++) { + match = false; + if (typeof fnRef == 'function' && hookPt[i] == fnRef) { + continue; + } else { + for(j = 0; j < fnRef.length; j++) if (hookPt[i] == fnRef[j]) { + match = true; + break; + } + } + if (!match) newPt[newPt.length++] = hookPt[i]; + } + + newPt[newPt.length++] = order; + + } else if (typeof order == 'object') { + if (typeof fnRef == 'object') { + newPt = newPt.concat(fnRef); + } else { + newPt[newPt.length++] = fnRef; + } + + for (j = 0; j < hookPt.length; j++) { + match = false; + if (typeof fnRef == 'function' && hookPt[j] == fnRef) { + continue; + } else { + for (i = 0; i < fnRef.length; i++) if (hookPt[j] == fnRef[i]) { + match = true; + break; + } + } + if (!match) newPt[newPt.length++]=hookPt[j]; + } + + for (i = 0; i < newPt.length; i++) hookPt[i] = newPt[i]; + newPt.length = 0; + + for (j = 0; j < hookPt.length; j++) { + match = false; + for (i = 0; i < order.length; i++) { + if (hookPt[j] == order[i]) { + match = true; + break; + } + } + if (!match) newPt[newPt.length++] = hookPt[j]; + } + newPt = newPt.concat(order); + } + + hookPt = newPt; + + return hookPt; +} + +//////// +// PLUGIN ACTIVATION FUNCTIONS +//////// + +// Runs plugin functions to set runtime variables. +function setRunTimeVariables(){ + if (typeof runTime != 'undefined' && runTime.length) { + for (var k = 0; k < runTime.length; k++) { + runTime[k](); + } + } +} + +// Runs plugin functions to parse commands. +function parseCmdLine(pf, i, args) { + if (typeof cmdLine != 'undefined' && cmdLine.length) { + for (var k = 0; k < cmdLine.length; k++) { + var j = cmdLine[k](pf, i, args); + if (j >- 1) { + i = j; + break; + } + } + } + + return i; +} + +// Runs plugin functions to do things after parse. +function postParseChecks(pf,args){ + if (typeof postParse != 'undefined' && postParse.length) { + for (var k = 0; k < postParse.length; k++) { + if (postParse[k](pf,args)) continue; + return false; // end now since have an error + } + } + return true; +} + + +//////// +// PLUGIN REGISTRATION FUNCTIONS +//////// + +// Registers commands and creates constants. +function registerCommands(cmdStr) { + if (typeof cmdStr!='string') return; + + var pM = cmdStr.split(','); + pms = pms.concat(pM); + + for (var i = 0; i< pM.length; i++) { + eval(pM[i].toUpperCase()+'='+pmCount++); + } +} + +// Registers no-parameter commands +function registerNoParameterCommands(cmdStr) { + if (!cmdStr && typeof cmdStr != 'string') return; + pmt=(!pmt) ? cmdStr : pmt + ',' + cmdStr; +} + +// Register a function to hook at a certain point. +function registerHook(fnHookTo, fnRef, hookType, optPm) { + var hookPt, last = typeof optPm; + + if (fnHookTo == 'plgIn'||fnHookTo == 'postParse') return; + if (typeof hookPts[fnHookTo] == 'undefined') hookPts[fnHookTo] = new FunctionReference(); + + hookPt = hookPts[fnHookTo]; + + if (hookType != null) { + if (hookType == FREPLACE) { + hookPt.ovload = fnRef; // replace normal overlib routine + if (fnHookTo.indexOf('ol_content_') > -1) hookPt.alt[pms[CSSOFF-1-pmStart]]=fnRef; + + } else if (hookType == FBEFORE || hookType == FAFTER) { + var hookPt=(hookType == 1 ? hookPt.before : hookPt.after); + + if (typeof fnRef == 'object') { + hookPt = hookPt.concat(fnRef); + } else { + hookPt[hookPt.length++] = fnRef; + } + + if (optPm) hookPt = reOrder(hookPt, fnRef, optPm); + + } else if (hookType == FALTERNATE) { + if (last=='number') hookPt.alt[pms[optPm-1-pmStart]] = fnRef; + } else if (hookType == FCHAIN) { + hookPt = hookPt.chain; + if (typeof fnRef=='object') hookPt=hookPt.concat(fnRef); // add other functions + else hookPt[hookPt.length++]=fnRef; + } + + return; + } +} + +// Register a function that will set runtime variables. +function registerRunTimeFunction(fn) { + if (isFunction(fn)) { + if (typeof fn == 'object') { + runTime = runTime.concat(fn); + } else { + runTime[runTime.length++] = fn; + } + } +} + +// Register a function that will handle command parsing. +function registerCmdLineFunction(fn){ + if (isFunction(fn)) { + if (typeof fn == 'object') { + cmdLine = cmdLine.concat(fn); + } else { + cmdLine[cmdLine.length++] = fn; + } + } +} + +// Register a function that does things after command parsing. +function registerPostParseFunction(fn){ + if (isFunction(fn)) { + if (typeof fn == 'object') { + postParse = postParse.concat(fn); + } else { + postParse[postParse.length++] = fn; + } + } +} + +//////// +// PLUGIN REGISTRATION FUNCTIONS +//////// + +// Runs any hooks registered. +function runHook(fnHookTo, hookType) { + var l = hookPts[fnHookTo], k, rtnVal = null, optPm, arS, ar = runHook.arguments; + + if (hookType == FREPLACE) { + arS = argToString(ar, 2); + + if (typeof l == 'undefined' || !(l = l.ovload)) rtnVal = eval(fnHookTo+'('+arS+')'); + else rtnVal = eval('l('+arS+')'); + + } else if (hookType == FBEFORE || hookType == FAFTER) { + if (typeof l != 'undefined') { + l=(hookType == 1 ? l.before : l.after); + + if (l.length) { + arS = argToString(ar, 2); + for (var k = 0; k < l.length; k++) eval('l[k]('+arS+')'); + } + } + } else if (hookType == FALTERNATE) { + optPm = ar[2]; + arS = argToString(ar, 3); + + if (typeof l == 'undefined' || (l = l.alt[pms[optPm-1-pmStart]]) == 'undefined') { + rtnVal = eval(fnHookTo+'('+arS+')'); + } else { + rtnVal = eval('l('+arS+')'); + } + } else if (hookType == FCHAIN) { + arS=argToString(ar,2); + l=l.chain; + + for (k=l.length; k > 0; k--) if((rtnVal=eval('l[k-1]('+arS+')'))!=void(0)) break; + } + + return rtnVal; +} + +//////// +// OBJECT CONSTRUCTORS +//////// + +// Object for handling hooks. +function FunctionReference() { + this.ovload = null; + this.before = new Array(); + this.after = new Array(); + this.alt = new Array(); + this.chain = new Array(); +} + +// Object for simple access to the overLIB version used. +// Examples: simpleversion:351 major:3 minor:5 revision:1 +function Info(version, prerelease) { + this.version = version; + this.prerelease = prerelease; + + this.simpleversion = Math.round(this.version*100); + this.major = parseInt(this.simpleversion / 100); + this.minor = parseInt(this.simpleversion / 10) - this.major * 10; + this.revision = parseInt(this.simpleversion) - this.major * 100 - this.minor * 10; + this.meets = meets; +} + +// checks for Core Version required +function meets(reqdVersion) { + return (!reqdVersion) ? false : this.simpleversion >= Math.round(100*parseFloat(reqdVersion)); +} + + +//////// +// STANDARD REGISTRATIONS +//////// +registerHook("ol_content_simple", ol_content_simple, FALTERNATE, CSSOFF); +registerHook("ol_content_caption", ol_content_caption, FALTERNATE, CSSOFF); +registerHook("ol_content_background", ol_content_background, FALTERNATE, CSSOFF); +registerHook("ol_content_simple", ol_content_simple, FALTERNATE, CSSCLASS); +registerHook("ol_content_caption", ol_content_caption, FALTERNATE, CSSCLASS); +registerHook("ol_content_background", ol_content_background, FALTERNATE, CSSCLASS); +registerPostParseFunction(checkPositionFlags); +registerHook("hideObject", nbspCleanup, FAFTER); +registerHook("horizontalPlacement", horizontalPlacement, FCHAIN); +registerHook("verticalPlacement", verticalPlacement, FCHAIN); +if (olNs4||(olIe5&&isMac)||olKq) olLoaded=1; +registerNoParameterCommands('sticky,autostatus,autostatuscap,fullhtml,hauto,vauto,closeclick,wrap,followmouse,mouseoff,compatmode'); +/////// +// ESTABLISH MOUSECAPTURING +/////// + +// Capture events, alt. diffuses the overlib function. +var olCheckMouseCapture=true; +if ((olNs4 || olNs6 || olIe4)) { + olMouseCapture(); +} else { + overlib = no_overlib; + nd = no_overlib; + ver3fix = true; +} diff --git a/test/src/translate.php b/test/src/translate.php new file mode 100644 index 0000000..e280fa6 --- /dev/null +++ b/test/src/translate.php @@ -0,0 +1,50 @@ +setlocale($language); +print "LOCALE: " . $language . " => " . $set_locale . "
"; +// Set default text domain +$loader->textdomain($domain); +// Set path where to look for a domain +$loader->bindtextdomain($domain, $locale_path); +// Get translator ($domain override) +$translator = $loader->getTranslator(); +// some translation +$translate = 'Original'; +$translated_mot = $translator->gettext($translate); +$translated_l10n = $l10n->__($translate); + +echo "ORIGINAL: " . $translate . "
"; +echo "TRANSLATED (MOT): " . $translated_mot . "
"; +echo "TRANSLATED (L10N): " . $translated_l10n . "
"; + +// __END__ diff --git a/test/templates_c/.gitignore b/test/templates_c/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/test/templates_c/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/test/tmp/.gitignore b/test/tmp/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/test/tmp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From 5bddc90972ed76b4806db71b2cc4cdabce7f1035 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Fri, 9 Aug 2024 17:40:22 +0900 Subject: [PATCH 02/10] Readme file update --- ReadMe.md | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 200 insertions(+), 11 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index ded4fce..c16496e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -22,25 +22,214 @@ Alternative setup composer local zip file repot: ## Install package -`composer require egrajp/smarty-extended:^4.3` +`composer require egrajp/smarty-extended:^5` ## How to update -1) update the original composer for ^4.3 -2) copy over the src/sysplugins and all base files in src/ -3) check either function.html_checkboxes.php and function.html_options.php have changed -4) copy src/plugins except the above two files, be sure to keep the block.t.php and function_popup*.php -5) Create new release version as official relase number +1) update the original composer for ^5 +2) copy over the following into `src/BlockHandler/`: + 1) T.php +3) copy over the following into `src/FunctionHandler`: + 1) Popup.php + 2) PopupInit.php +4) Upate the global `src/Extensions/DefaultExtension.php`: + 1) `getFunctionHandler`: popup_init, popup + 2) `getBlockHandler`: t +5) check either `src/FunctionHander/HtmlCheckboxes.php` and `src/FunctionHander/HtmlOptions.php` have changed + 1) Update and leep the label/pos changes +6) Create new release version as official relase number ## Updated files (different from master) ### New -`src/plugins/block.t.php` -`src/plugins/function_popup.php` -`src/plugins/function_popup.init.php` +- `src/BlockHandler/T.php` +- `src/FunctionHandler/Popup.php` +- `src/FunctionHandler/PopupInit.php` ### Changed -`src/plugins/function.html_checkboxes.php` -`src/plugins/function.html_options.php` +- `src/FunctionHander/HtmlCheckboxes.php` + +```diff +--- Smarty/Smarty-git/src/FunctionHandler/HtmlCheckboxes.php 2024-04-16 18:06:25.299206501 +0900 ++++ core_data/composer-packages/Smarty-Extended/src/FunctionHandler/HtmlCheckboxes.php 2024-07-26 11:48:23.698784159 +0900 +@@ -24,6 +24,7 @@ + * - checked (optional) - array default not set + * - separator (optional) - ie
or   + * - output (optional) - the output next to each checkbox ++ * - pos (optional) - position entry into the [] for multi checkboxes + * - assign (optional) - assign the output as an array to this variable + * - escape (optional) - escape the content (not value), defaults to true + * +@@ -50,6 +51,7 @@ + $labels = true; + $label_ids = false; + $output = null; ++ $pos = null; + $extra = ''; + foreach ($params as $_key => $_val) { + switch ($_key) { +@@ -111,6 +113,9 @@ + ); + $options = (array)$_val; + break; ++ case 'pos': ++ $$_key = array_values((array)$_val); ++ break; + case 'strict': + case 'assign': + break; +@@ -145,6 +150,7 @@ + $_html_result = []; + if (isset($options)) { + foreach ($options as $_key => $_val) { ++ $_pos = isset($pos[ $_key ]) ? $pos[ $_key ] : ''; + $_html_result[] = + $this->getHtmlForInput( + 'checkbox', +@@ -157,12 +163,14 @@ + $separator, + $labels, + $label_ids, ++ $_pos, + $escape + ); + } + } else { + foreach ($values as $_i => $_key) { + $_val = isset($output[$_i]) ? $output[$_i] : ''; ++ $_pos = isset($pos[ $_i ]) ? $pos[ $_i ] : ''; + $_html_result[] = + $this->getHtmlForInput( + 'checkbox', +@@ -175,6 +183,7 @@ + $separator, + $labels, + $label_ids, ++ $_pos, + $escape + ); + } +``` + +- `src/FunctionHander/HtmlOptions.php` + +```diff +--- Smarty/Smarty-git/src/FunctionHandler/HtmlOptions.php 2024-04-16 18:06:25.299206501 +0900 ++++ core_data/composer-packages/Smarty-Extended/src/FunctionHandler/HtmlOptions.php 2024-07-26 11:51:13.287320709 +0900 +@@ -17,6 +17,7 @@ + * - selected (optional) - string default not set + * - output (required) - if not options supplied) - array + * - id (optional) - string default not set ++ * - label (optional) - label strinng to set + * - class (optional) - string default not set + * + * @author Monte Ohrt +@@ -40,6 +41,7 @@ + $output = null; + $id = null; + $class = null; ++ $label = true; + $extra = ''; + foreach ($params as $_key => $_val) { + switch ($_key) { +@@ -89,6 +91,11 @@ + $selected = smarty_function_escape_special_chars((string)$_val); + } + break; ++ case 'label': ++ if ($_val == 'true' || $_val == 'false') { ++ $$_key = (string)$_val; ++ } ++ break; + case 'strict': + break; + case 'disabled': +@@ -124,12 +131,12 @@ + $_idx = 0; + if (isset($options)) { + foreach ($options as $_key => $_val) { +- $_html_result .= $this->output($_key, $_val, $selected, $id, $class, $_idx); ++ $_html_result .= $this->output($_key, $_val, $selected, $id, $class, $label, $_idx); + } + } else { + foreach ($values as $_i => $_key) { + $_val = $output[$_i] ?? ''; +- $_html_result .= $this->output($_key, $_val, $selected, $id, $class, $_idx); ++ $_html_result .= $this->output($_key, $_val, $selected, $id, $class, $label, $_idx); + } + } + if (!empty($name)) { +@@ -149,15 +156,20 @@ + * @param $selected + * @param $id + * @param $class ++ * @param $label + * @param $idx + * + * @return string + */ +- private function output($key, $value, $selected, $id, $class, &$idx) ++ private function output($key, $value, $selected, $id, $class, $label, &$idx) + { + if (!is_array($value)) { + $_key = smarty_function_escape_special_chars($key); +- $_html_result = '
'; - else { - fontStr='o3_'+whichString+'font'; - fontColor='o3_'+((whichString=='caption')? 'cap' : whichString)+'color'; - return (hasDims&&!olNs4) ? (isClose ? '' : '
') : ''; - } -} - -// Quotes Multi word font names; needed for CSS Standards adherence in font-family -function quoteMultiNameFonts(theFont) { - var v, pM=theFont.split(','); - for (var i=0; i 0) clearTimeout(o3_timerid); - - o3_timerid=setTimeout("cClick()",(o3_timeout=time)); - } -} - -// Was originally in the placeLayer() routine; separated out for future ease -function horizontalPlacement(browserWidth, horizontalScrollAmount, widthFix) { - var placeX, iwidth=browserWidth, winoffset=horizontalScrollAmount; - var parsedWidth = parseInt(o3_width); - - if (o3_fixx > -1 || o3_relx != null) { - // Fixed position - placeX=(o3_relx != null ? ( o3_relx < 0 ? winoffset +o3_relx+ iwidth - parsedWidth - widthFix : winoffset+o3_relx) : o3_fixx); - } else { - // If HAUTO, decide what to use. - if (o3_hauto == 1) { - if ((o3_x - winoffset) > (iwidth / 2)) { - o3_hpos = LEFT; - } else { - o3_hpos = RIGHT; - } - } - - // From mouse - if (o3_hpos == CENTER) { // Center - placeX = o3_x+o3_offsetx-(parsedWidth/2); - - if (placeX < winoffset) placeX = winoffset; - } - - if (o3_hpos == RIGHT) { // Right - placeX = o3_x+o3_offsetx; - - if ((placeX+parsedWidth) > (winoffset+iwidth - widthFix)) { - placeX = iwidth+winoffset - parsedWidth - widthFix; - if (placeX < 0) placeX = 0; - } - } - if (o3_hpos == LEFT) { // Left - placeX = o3_x-o3_offsetx-parsedWidth; - if (placeX < winoffset) placeX = winoffset; - } - - // Snapping! - if (o3_snapx > 1) { - var snapping = placeX % o3_snapx; - - if (o3_hpos == LEFT) { - placeX = placeX - (o3_snapx+snapping); - } else { - // CENTER and RIGHT - placeX = placeX+(o3_snapx - snapping); - } - - if (placeX < winoffset) placeX = winoffset; - } - } - - return placeX; -} - -// was originally in the placeLayer() routine; separated out for future ease -function verticalPlacement(browserHeight,verticalScrollAmount) { - var placeY, iheight=browserHeight, scrolloffset=verticalScrollAmount; - var parsedHeight=(o3_aboveheight ? parseInt(o3_aboveheight) : (olNs4 ? over.clip.height : over.offsetHeight)); - - if (o3_fixy > -1 || o3_rely != null) { - // Fixed position - placeY=(o3_rely != null ? (o3_rely < 0 ? scrolloffset+o3_rely+iheight - parsedHeight : scrolloffset+o3_rely) : o3_fixy); - } else { - // If VAUTO, decide what to use. - if (o3_vauto == 1) { - if ((o3_y - scrolloffset) > (iheight / 2) && o3_vpos == BELOW && (o3_y + parsedHeight + o3_offsety - (scrolloffset + iheight) > 0)) { - o3_vpos = ABOVE; - } else if (o3_vpos == ABOVE && (o3_y - (parsedHeight + o3_offsety) - scrolloffset < 0)) { - o3_vpos = BELOW; - } - } - - // From mouse - if (o3_vpos == ABOVE) { - if (o3_aboveheight == 0) o3_aboveheight = parsedHeight; - - placeY = o3_y - (o3_aboveheight+o3_offsety); - if (placeY < scrolloffset) placeY = scrolloffset; - } else { - // BELOW - placeY = o3_y+o3_offsety; - } - - // Snapping! - if (o3_snapy > 1) { - var snapping = placeY % o3_snapy; - - if (o3_aboveheight > 0 && o3_vpos == ABOVE) { - placeY = placeY - (o3_snapy+snapping); - } else { - placeY = placeY+(o3_snapy - snapping); - } - - if (placeY < scrolloffset) placeY = scrolloffset; - } - } - - return placeY; -} - -// checks positioning flags -function checkPositionFlags() { - if (olHautoFlag) olHautoFlag = o3_hauto=0; - if (olVautoFlag) olVautoFlag = o3_vauto=0; - return true; -} - -// get Browser window width -function windowWidth() { - var w; - if (o3_frame.innerWidth) w=o3_frame.innerWidth; - else if (eval('o3_frame.'+docRoot)&&eval("typeof o3_frame."+docRoot+".clientWidth=='number'")&&eval('o3_frame.'+docRoot+'.clientWidth')) - w=eval('o3_frame.'+docRoot+'.clientWidth'); - return w; -} - -// create the div container for popup content if it doesn't exist -function createDivContainer(id,frm,zValue) { - id = (id || 'overDiv'), frm = (frm || o3_frame), zValue = (zValue || 1000); - var objRef, divContainer = layerReference(id); - - if (divContainer == null) { - if (olNs4) { - divContainer = frm.document.layers[id] = new Layer(window.innerWidth, frm); - objRef = divContainer; - } else { - var body = (olIe4 ? frm.document.all.tags('BODY')[0] : frm.document.getElementsByTagName("BODY")[0]); - if (olIe4&&!document.getElementById) { - body.insertAdjacentHTML("beforeEnd",'
'); - divContainer=layerReference(id); - } else { - divContainer = frm.document.createElement("DIV"); - divContainer.id = id; - body.appendChild(divContainer); - } - objRef = divContainer.style; - } - - objRef.position = 'absolute'; - objRef.visibility = 'hidden'; - objRef.zIndex = zValue; - if (olIe4&&!olOp) objRef.left = objRef.top = '0px'; - else objRef.left = objRef.top = -10000 + (!olNs4 ? 'px' : 0); - } - - return divContainer; -} - -// get reference to a layer with ID=id -function layerReference(id) { - return (olNs4 ? o3_frame.document.layers[id] : (document.all ? o3_frame.document.all[id] : o3_frame.document.getElementById(id))); -} -//////// -// UTILITY FUNCTIONS -//////// - -// Checks if something is a function. -function isFunction(fnRef) { - var rtn = true; - - if (typeof fnRef == 'object') { - for (var i = 0; i < fnRef.length; i++) { - if (typeof fnRef[i]=='function') continue; - rtn = false; - break; - } - } else if (typeof fnRef != 'function') { - rtn = false; - } - - return rtn; -} - -// Converts an array into an argument string for use in eval. -function argToString(array, strtInd, argName) { - var jS = strtInd, aS = '', ar = array; - argName=(argName ? argName : 'ar'); - - if (ar.length > jS) { - for (var k = jS; k < ar.length; k++) aS += argName+'['+k+'], '; - aS = aS.substring(0, aS.length-2); - } - - return aS; -} - -// Places a hook in the correct position in a hook point. -function reOrder(hookPt, fnRef, order) { - var newPt = new Array(), match, i, j; - - if (!order || typeof order == 'undefined' || typeof order == 'number') return hookPt; - - if (typeof order=='function') { - if (typeof fnRef=='object') { - newPt = newPt.concat(fnRef); - } else { - newPt[newPt.length++]=fnRef; - } - - for (i = 0; i < hookPt.length; i++) { - match = false; - if (typeof fnRef == 'function' && hookPt[i] == fnRef) { - continue; - } else { - for(j = 0; j < fnRef.length; j++) if (hookPt[i] == fnRef[j]) { - match = true; - break; - } - } - if (!match) newPt[newPt.length++] = hookPt[i]; - } - - newPt[newPt.length++] = order; - - } else if (typeof order == 'object') { - if (typeof fnRef == 'object') { - newPt = newPt.concat(fnRef); - } else { - newPt[newPt.length++] = fnRef; - } - - for (j = 0; j < hookPt.length; j++) { - match = false; - if (typeof fnRef == 'function' && hookPt[j] == fnRef) { - continue; - } else { - for (i = 0; i < fnRef.length; i++) if (hookPt[j] == fnRef[i]) { - match = true; - break; - } - } - if (!match) newPt[newPt.length++]=hookPt[j]; - } - - for (i = 0; i < newPt.length; i++) hookPt[i] = newPt[i]; - newPt.length = 0; - - for (j = 0; j < hookPt.length; j++) { - match = false; - for (i = 0; i < order.length; i++) { - if (hookPt[j] == order[i]) { - match = true; - break; - } - } - if (!match) newPt[newPt.length++] = hookPt[j]; - } - newPt = newPt.concat(order); - } - - hookPt = newPt; - - return hookPt; -} - -//////// -// PLUGIN ACTIVATION FUNCTIONS -//////// - -// Runs plugin functions to set runtime variables. -function setRunTimeVariables(){ - if (typeof runTime != 'undefined' && runTime.length) { - for (var k = 0; k < runTime.length; k++) { - runTime[k](); - } - } -} - -// Runs plugin functions to parse commands. -function parseCmdLine(pf, i, args) { - if (typeof cmdLine != 'undefined' && cmdLine.length) { - for (var k = 0; k < cmdLine.length; k++) { - var j = cmdLine[k](pf, i, args); - if (j >- 1) { - i = j; - break; - } - } - } - - return i; -} - -// Runs plugin functions to do things after parse. -function postParseChecks(pf,args){ - if (typeof postParse != 'undefined' && postParse.length) { - for (var k = 0; k < postParse.length; k++) { - if (postParse[k](pf,args)) continue; - return false; // end now since have an error - } - } - return true; -} - - -//////// -// PLUGIN REGISTRATION FUNCTIONS -//////// - -// Registers commands and creates constants. -function registerCommands(cmdStr) { - if (typeof cmdStr!='string') return; - - var pM = cmdStr.split(','); - pms = pms.concat(pM); - - for (var i = 0; i< pM.length; i++) { - eval(pM[i].toUpperCase()+'='+pmCount++); - } -} - -// Registers no-parameter commands -function registerNoParameterCommands(cmdStr) { - if (!cmdStr && typeof cmdStr != 'string') return; - pmt=(!pmt) ? cmdStr : pmt + ',' + cmdStr; -} - -// Register a function to hook at a certain point. -function registerHook(fnHookTo, fnRef, hookType, optPm) { - var hookPt, last = typeof optPm; - - if (fnHookTo == 'plgIn'||fnHookTo == 'postParse') return; - if (typeof hookPts[fnHookTo] == 'undefined') hookPts[fnHookTo] = new FunctionReference(); - - hookPt = hookPts[fnHookTo]; - - if (hookType != null) { - if (hookType == FREPLACE) { - hookPt.ovload = fnRef; // replace normal overlib routine - if (fnHookTo.indexOf('ol_content_') > -1) hookPt.alt[pms[CSSOFF-1-pmStart]]=fnRef; - - } else if (hookType == FBEFORE || hookType == FAFTER) { - var hookPt=(hookType == 1 ? hookPt.before : hookPt.after); - - if (typeof fnRef == 'object') { - hookPt = hookPt.concat(fnRef); - } else { - hookPt[hookPt.length++] = fnRef; - } - - if (optPm) hookPt = reOrder(hookPt, fnRef, optPm); - - } else if (hookType == FALTERNATE) { - if (last=='number') hookPt.alt[pms[optPm-1-pmStart]] = fnRef; - } else if (hookType == FCHAIN) { - hookPt = hookPt.chain; - if (typeof fnRef=='object') hookPt=hookPt.concat(fnRef); // add other functions - else hookPt[hookPt.length++]=fnRef; - } - - return; - } -} - -// Register a function that will set runtime variables. -function registerRunTimeFunction(fn) { - if (isFunction(fn)) { - if (typeof fn == 'object') { - runTime = runTime.concat(fn); - } else { - runTime[runTime.length++] = fn; - } - } -} - -// Register a function that will handle command parsing. -function registerCmdLineFunction(fn){ - if (isFunction(fn)) { - if (typeof fn == 'object') { - cmdLine = cmdLine.concat(fn); - } else { - cmdLine[cmdLine.length++] = fn; - } - } -} - -// Register a function that does things after command parsing. -function registerPostParseFunction(fn){ - if (isFunction(fn)) { - if (typeof fn == 'object') { - postParse = postParse.concat(fn); - } else { - postParse[postParse.length++] = fn; - } - } -} - -//////// -// PLUGIN REGISTRATION FUNCTIONS -//////// - -// Runs any hooks registered. -function runHook(fnHookTo, hookType) { - var l = hookPts[fnHookTo], k, rtnVal = null, optPm, arS, ar = runHook.arguments; - - if (hookType == FREPLACE) { - arS = argToString(ar, 2); - - if (typeof l == 'undefined' || !(l = l.ovload)) rtnVal = eval(fnHookTo+'('+arS+')'); - else rtnVal = eval('l('+arS+')'); - - } else if (hookType == FBEFORE || hookType == FAFTER) { - if (typeof l != 'undefined') { - l=(hookType == 1 ? l.before : l.after); - - if (l.length) { - arS = argToString(ar, 2); - for (var k = 0; k < l.length; k++) eval('l[k]('+arS+')'); - } - } - } else if (hookType == FALTERNATE) { - optPm = ar[2]; - arS = argToString(ar, 3); - - if (typeof l == 'undefined' || (l = l.alt[pms[optPm-1-pmStart]]) == 'undefined') { - rtnVal = eval(fnHookTo+'('+arS+')'); - } else { - rtnVal = eval('l('+arS+')'); - } - } else if (hookType == FCHAIN) { - arS=argToString(ar,2); - l=l.chain; - - for (k=l.length; k > 0; k--) if((rtnVal=eval('l[k-1]('+arS+')'))!=void(0)) break; - } - - return rtnVal; -} - -//////// -// OBJECT CONSTRUCTORS -//////// - -// Object for handling hooks. -function FunctionReference() { - this.ovload = null; - this.before = new Array(); - this.after = new Array(); - this.alt = new Array(); - this.chain = new Array(); -} - -// Object for simple access to the overLIB version used. -// Examples: simpleversion:351 major:3 minor:5 revision:1 -function Info(version, prerelease) { - this.version = version; - this.prerelease = prerelease; - - this.simpleversion = Math.round(this.version*100); - this.major = parseInt(this.simpleversion / 100); - this.minor = parseInt(this.simpleversion / 10) - this.major * 10; - this.revision = parseInt(this.simpleversion) - this.major * 100 - this.minor * 10; - this.meets = meets; -} - -// checks for Core Version required -function meets(reqdVersion) { - return (!reqdVersion) ? false : this.simpleversion >= Math.round(100*parseFloat(reqdVersion)); -} - - -//////// -// STANDARD REGISTRATIONS -//////// -registerHook("ol_content_simple", ol_content_simple, FALTERNATE, CSSOFF); -registerHook("ol_content_caption", ol_content_caption, FALTERNATE, CSSOFF); -registerHook("ol_content_background", ol_content_background, FALTERNATE, CSSOFF); -registerHook("ol_content_simple", ol_content_simple, FALTERNATE, CSSCLASS); -registerHook("ol_content_caption", ol_content_caption, FALTERNATE, CSSCLASS); -registerHook("ol_content_background", ol_content_background, FALTERNATE, CSSCLASS); -registerPostParseFunction(checkPositionFlags); -registerHook("hideObject", nbspCleanup, FAFTER); -registerHook("horizontalPlacement", horizontalPlacement, FCHAIN); -registerHook("verticalPlacement", verticalPlacement, FCHAIN); -if (olNs4||(olIe5&&isMac)||olKq) olLoaded=1; -registerNoParameterCommands('sticky,autostatus,autostatuscap,fullhtml,hauto,vauto,closeclick,wrap,followmouse,mouseoff,compatmode'); -/////// -// ESTABLISH MOUSECAPTURING -/////// - -// Capture events, alt. diffuses the overlib function. -var olCheckMouseCapture=true; -if ((olNs4 || olNs6 || olIe4)) { - olMouseCapture(); -} else { - overlib = no_overlib; - nd = no_overlib; - ver3fix = true; -} diff --git a/test/src/translate.php b/test/src/translate.php deleted file mode 100644 index e280fa6..0000000 --- a/test/src/translate.php +++ /dev/null @@ -1,50 +0,0 @@ -setlocale($language); -print "LOCALE: " . $language . " => " . $set_locale . "
"; -// Set default text domain -$loader->textdomain($domain); -// Set path where to look for a domain -$loader->bindtextdomain($domain, $locale_path); -// Get translator ($domain override) -$translator = $loader->getTranslator(); -// some translation -$translate = 'Original'; -$translated_mot = $translator->gettext($translate); -$translated_l10n = $l10n->__($translate); - -echo "ORIGINAL: " . $translate . "
"; -echo "TRANSLATED (MOT): " . $translated_mot . "
"; -echo "TRANSLATED (L10N): " . $translated_l10n . "
"; - -// __END__ From b390d0f67a41712e617bbbd11207a8199995160f Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 20 Aug 2024 11:05:36 +0900 Subject: [PATCH 06/10] Readme update for Smarty v5 update process --- ReadMe.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index c16496e..6d98c37 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -27,17 +27,20 @@ Alternative setup composer local zip file repot: ## How to update 1) update the original composer for ^5 -2) copy over the following into `src/BlockHandler/`: + 1) Located in `Smarty/Smarty-Composer/vendor/smarty/smarty/src/` +2) Alternative is to checkout master branch from git + 1) Located in `Smarty/Smarty-git/src/` +3) copy over the following into `src/BlockHandler/`: 1) T.php -3) copy over the following into `src/FunctionHandler`: +4) copy over the following into `src/FunctionHandler`: 1) Popup.php 2) PopupInit.php -4) Upate the global `src/Extensions/DefaultExtension.php`: +5) Upate the global `src/Extensions/DefaultExtension.php`: 1) `getFunctionHandler`: popup_init, popup 2) `getBlockHandler`: t -5) check either `src/FunctionHander/HtmlCheckboxes.php` and `src/FunctionHander/HtmlOptions.php` have changed +6) check either `src/FunctionHander/HtmlCheckboxes.php` and `src/FunctionHander/HtmlOptions.php` have changed 1) Update and leep the label/pos changes -6) Create new release version as official relase number +7) Create new release version as official relase number ## Updated files (different from master) From 4ab8a6810c5d67a332aea7658962a4f94a1826a8 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 20 Aug 2024 11:11:25 +0900 Subject: [PATCH 07/10] Deploy Script update Fix all shellcheck problems --- .shellcheckrc | 2 ++ publish/publish.sh | 33 +++++++++++++++++---------------- 2 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 .shellcheckrc diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000..b14745b --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +shell=bash +external-sources=true diff --git a/publish/publish.sh b/publish/publish.sh index 4f58fcc..c56ac3e 100755 --- a/publish/publish.sh +++ b/publish/publish.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BASE_FOLDER=$(dirname $(readlink -f $0))"/"; +BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/"; PACKAGE_DOWNLOAD="${BASE_FOLDER}package-download/"; if [ ! -d "${PACKAGE_DOWNLOAD}" ]; then mkdir "${PACKAGE_DOWNLOAD}"; @@ -15,8 +15,8 @@ if [ -z "${VERSION}" ]; then fi; # compare version, if different or newer, deploy if [ -f "${file_last_published}" ]; then - LAST_PUBLISHED_VERSION=$(cat ${file_last_published}); - if $(dpkg --compare-versions "${VERSION}" le "${LAST_PUBLISHED_VERSION}"); then + LAST_PUBLISHED_VERSION=$(cat "${file_last_published}"); + if dpkg --compare-versions "${VERSION}" le "${LAST_PUBLISHED_VERSION}"; then echo "git tag version ${VERSION} is not newer than previous published version ${LAST_PUBLISHED_VERSION}"; exit; fi; @@ -40,9 +40,10 @@ if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then exit; fi; set -o allexport; -cd ${BASE_FOLDER}; +cd "${BASE_FOLDER}" || exit; +# shellcheck source=.env.deploy source .env.deploy; -cd -; +cd - || exit; set +o allexport; if [ "${go_flag}" != "go" ]; then @@ -55,21 +56,21 @@ fi; echo "[START]"; # gitea # skip iof -if [ ! -z "${GITEA_PUBLISH}" ]; then - if [ ! -z "${GITEA_UPLOAD_FILENAME}" ] && - [ ! -z "${GITEA_URL_DL}" ] && [ ! -z "${GITEA_URL_PUSH}" ] && - [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then +if [ -n "${GITEA_PUBLISH}" ]; then + if [ -n "${GITEA_UPLOAD_FILENAME}" ] && + [ -n "${GITEA_URL_DL}" ] && [ -n "${GITEA_URL_PUSH}" ] && + [ -n "${GITEA_USER}" ] && [ -n "${GITEA_TOKEN}" ]; then if [ ! -f "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" ]; then curl -LJO \ --output-dir "${PACKAGE_DOWNLOAD}" \ - ${GITEA_URL_DL}/v${VERSION}.zip; + "${GITEA_URL_DL}"/v"${VERSION}".zip; fi; if [ ! -f "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" ]; then echo "Version file does not exist for ${VERSION}"; else - curl --user ${GITEA_USER}:${GITEA_TOKEN} \ + curl --user "${GITEA_USER}":"${GITEA_TOKEN}" \ --upload-file "${PACKAGE_DOWNLOAD}${GITEA_UPLOAD_FILENAME}-v${VERSION}.zip" \ - ${GITEA_URL_PUSH}?version=${VERSION}; + "${GITEA_URL_PUSH}"?version="${VERSION}"; echo "${VERSION}" > "${file_last_published}"; fi; else @@ -78,9 +79,9 @@ if [ ! -z "${GITEA_PUBLISH}" ]; then fi; # gitlab -if [ ! -z "${GITLAB_PUBLISH}" ]; then - if [ ! -z "${GITLAB_URL}" ] && [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then - curl --data tag=v${VERSION} \ +if [ -n "${GITLAB_PUBLISH}" ]; then + if [ -n "${GITLAB_URL}" ] && [ -n "${GITLAB_DEPLOY_TOKEN}" ]; then + curl --data tag=v"${VERSION}" \ --header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \ "${GITLAB_URL}"; curl --data branch=master \ @@ -88,7 +89,7 @@ if [ ! -z "${GITLAB_PUBLISH}" ]; then "${GITLAB_URL}"; echo "${VERSION}" > "${file_last_published}"; else - echo "Missing GITLAB_DEPLOY_TOKEN environment variable"; + echo "Missing GITLAB_URL or GITLAB_DEPLOY_TOKEN environment variable"; fi; fi; echo ""; From f7faa504f86a210be8979e8f9718ae689f077d4e Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 20 Aug 2024 11:19:19 +0900 Subject: [PATCH 08/10] Update documentation and composer file with correct git repo Homepage The correct place is at the EGRA github org, reference original in the description. Update the require part for the composer json file with this new information --- ReadMe.md | 5 ++++- composer.json | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 6d98c37..29ba133 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,6 +1,6 @@ # Composer package from Smarty Extended -This is an updated package for smarty\smarty +This is an updated package for [smarty\smarty](https://github.com/smarty-php/smarty/) Adds: @@ -20,6 +20,9 @@ composer config repositories.git.egplusww.jp.Composer composer https://git.egplu Alternative setup composer local zip file repot: `composer config repositories.composer.egplusww.jp composer http://composer.egplusww.jp` +> [!notice] +> Requires mbstring extension installed, will not use the symfony/polyfill-mbstring + ## Install package `composer require egrajp/smarty-extended:^5` diff --git a/composer.json b/composer.json index 13b7c70..606b20f 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,11 @@ { "name": "egrajp/smarty-extended", - "description": "Smarty, extended with gettext, checkbox/radio labels and index numbers", + "description": "Smarty, extended with gettext, checkbox/radio labels and index numbers. Based on the smarty project: https://github.com/smarty-php/smarty/", "type": "library", "keywords": [ "templating" ], - "homepage": "https://github.com/smarty-php/smarty/", + "homepage": "https://github.com/TBWA-EGPlus-Japan/Composer.smarty-extended", "license": "LGPL-3.0", "autoload": { "files": [ @@ -28,7 +28,11 @@ "url": "https://git.egplusww.jp/api/packages/Composer/composer" } }, + "require": { + "php": "^7.4 || ^8.0", + "ext-mbstring": "*" + }, "require-dev": { - "egrajp/corelibs-composer-all": "^9.13" + "egrajp/corelibs-composer-all": "^9" } } From fd369a7115cc350bfac75950f4de527311e8cc9c Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 26 Dec 2024 17:59:29 +0900 Subject: [PATCH 09/10] Smarty Update to v5.4.3 --- ReadMe.md | 24 +- src/BlockHandler/BlockPluginWrapper.php | 2 +- src/Cacheresource/Base.php | 2 +- src/Cacheresource/Custom.php | 2 +- src/Cacheresource/File.php | 2 +- src/Cacheresource/KeyValueStore.php | 2 +- src/Compiler/CodeFrame.php | 2 +- src/Compiler/Template.php | 14 +- src/CompilerException.php | 4 +- src/Extension/CallbackWrapper.php | 2 +- src/FunctionHandler/HtmlBase.php | 6 +- src/Parser/TemplateParser.php | 1912 +++++++++-------- src/Parser/TemplateParser.y | 140 +- src/Resource/BasePlugin.php | 2 +- src/Resource/CustomPlugin.php | 2 +- src/Resource/ExtendsPlugin.php | 8 +- src/Resource/FilePlugin.php | 2 +- src/Resource/StreamPlugin.php | 2 +- src/Resource/StringPlugin.php | 2 +- src/Runtime/DefaultPluginHandlerRuntime.php | 4 +- src/Runtime/InheritanceRuntime.php | 2 +- src/Smarty.php | 2 +- src/Template.php | 6 +- src/Template/Source.php | 8 +- src/TemplateBase.php | 2 +- update/ReadMe.md | 15 + update/src/BlockHandler/T.php | 238 ++ update/src/Extensions/DefaultExtension.php | 760 +++++++ update/src/FunctionHandler/HtmlCheckboxes.php | 198 ++ update/src/FunctionHandler/HtmlOptions.php | 236 ++ update/src/FunctionHandler/Popup.php | 123 ++ update/src/FunctionHandler/PopupInit.php | 36 + 32 files changed, 2785 insertions(+), 977 deletions(-) create mode 100644 update/ReadMe.md create mode 100644 update/src/BlockHandler/T.php create mode 100644 update/src/Extensions/DefaultExtension.php create mode 100644 update/src/FunctionHandler/HtmlCheckboxes.php create mode 100644 update/src/FunctionHandler/HtmlOptions.php create mode 100644 update/src/FunctionHandler/Popup.php create mode 100644 update/src/FunctionHandler/PopupInit.php diff --git a/ReadMe.md b/ReadMe.md index 29ba133..7882e28 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -33,17 +33,19 @@ Alternative setup composer local zip file repot: 1) Located in `Smarty/Smarty-Composer/vendor/smarty/smarty/src/` 2) Alternative is to checkout master branch from git 1) Located in `Smarty/Smarty-git/src/` -3) copy over the following into `src/BlockHandler/`: - 1) T.php -4) copy over the following into `src/FunctionHandler`: - 1) Popup.php - 2) PopupInit.php -5) Upate the global `src/Extensions/DefaultExtension.php`: - 1) `getFunctionHandler`: popup_init, popup - 2) `getBlockHandler`: t -6) check either `src/FunctionHander/HtmlCheckboxes.php` and `src/FunctionHander/HtmlOptions.php` have changed - 1) Update and leep the label/pos changes -7) Create new release version as official relase number +3) Copy the `src/` folder as is over the `Smarty/Smarty-Extended/src` folder +4) From the `update/` folder copy + 1) copy over the following into `src/BlockHandler/`: + 1) T.php + 2) copy over the following into `src/FunctionHandler`: + 1) Popup.php + 2) PopupInit.php + 3) Upate the global `src/Extensions/DefaultExtension.php`: + 1) `getFunctionHandler`: popup_init, popup + 2) `getBlockHandler`: t + 4) check either `src/FunctionHander/HtmlCheckboxes.php` and `src/FunctionHander/HtmlOptions.php` have changed + 1) Update and leep the label/pos changes +5) Create new release version as official relase number ## Updated files (different from master) diff --git a/src/BlockHandler/BlockPluginWrapper.php b/src/BlockHandler/BlockPluginWrapper.php index 4700594..b6236a6 100644 --- a/src/BlockHandler/BlockPluginWrapper.php +++ b/src/BlockHandler/BlockPluginWrapper.php @@ -14,6 +14,6 @@ class BlockPluginWrapper extends Base { } public function handle($params, $content, Template $template, &$repeat) { - return call_user_func_array($this->callback, [$params, $content, &$template, &$repeat]); + return \call_user_func_array($this->callback, [$params, $content, &$template, &$repeat]); } } \ No newline at end of file diff --git a/src/Cacheresource/Base.php b/src/Cacheresource/Base.php index 54a1419..41c4f6b 100644 --- a/src/Cacheresource/Base.php +++ b/src/Cacheresource/Base.php @@ -44,7 +44,7 @@ abstract class Base */ abstract public function process( Template $_template, - Cached $cached = null, + ?Cached $cached = null, $update = false ); diff --git a/src/Cacheresource/Custom.php b/src/Cacheresource/Custom.php index c049246..f9eb858 100644 --- a/src/Cacheresource/Custom.php +++ b/src/Cacheresource/Custom.php @@ -139,7 +139,7 @@ abstract class Custom extends Base */ public function process( Template $_smarty_tpl, - \Smarty\Template\Cached $cached = null, + ?\Smarty\Template\Cached $cached = null, $update = false ) { if (!$cached) { diff --git a/src/Cacheresource/File.php b/src/Cacheresource/File.php index 538f010..4b4198e 100644 --- a/src/Cacheresource/File.php +++ b/src/Cacheresource/File.php @@ -99,7 +99,7 @@ class File extends Base */ public function process( Template $_smarty_tpl, - Cached $cached = null, + ?Cached $cached = null, $update = false ) { $_smarty_tpl->getCached()->setValid(false); diff --git a/src/Cacheresource/KeyValueStore.php b/src/Cacheresource/KeyValueStore.php index 733d277..1953bb2 100644 --- a/src/Cacheresource/KeyValueStore.php +++ b/src/Cacheresource/KeyValueStore.php @@ -103,7 +103,7 @@ abstract class KeyValueStore extends Base */ public function process( Template $_smarty_tpl, - Cached $cached = null, + ?Cached $cached = null, $update = false ) { if (!$cached) { diff --git a/src/Compiler/CodeFrame.php b/src/Compiler/CodeFrame.php index 5e78215..d0e1cc1 100644 --- a/src/Compiler/CodeFrame.php +++ b/src/Compiler/CodeFrame.php @@ -41,7 +41,7 @@ class CodeFrame $content = '', $functions = '', $cache = false, - \Smarty\Compiler\Template $compiler = null + ?\Smarty\Compiler\Template $compiler = null ) { // build property code $properties[ 'version' ] = \Smarty\Smarty::SMARTY_VERSION; diff --git a/src/Compiler/Template.php b/src/Compiler/Template.php index 9b2c1a1..237407c 100644 --- a/src/Compiler/Template.php +++ b/src/Compiler/Template.php @@ -374,7 +374,7 @@ class Template extends BaseCompiler { * @throws CompilerException * @throws Exception */ - public function compileTemplateSource(\Smarty\Template $template, \Smarty\Compiler\Template $parent_compiler = null) { + public function compileTemplateSource(\Smarty\Template $template, ?\Smarty\Compiler\Template $parent_compiler = null) { try { // save template object in compiler class $this->template = $template; @@ -505,7 +505,7 @@ class Template extends BaseCompiler { * * @return string */ - public function compileVariable($variable) { + public function triggerTagNoCache($variable): void { if (!strpos($variable, '(')) { // not a variable variable $var = trim($variable, '\''); @@ -516,7 +516,6 @@ class Template extends BaseCompiler { false )->isNocache(); } - return '$_smarty_tpl->getValue(' . $variable . ')'; } /** @@ -665,7 +664,7 @@ class Template extends BaseCompiler { $script = null; $cacheable = true; - $result = call_user_func_array( + $result = \call_user_func_array( $defaultPluginHandlerFunc, [ $tag, @@ -1281,9 +1280,10 @@ class Template extends BaseCompiler { } // call post compile callbacks foreach ($this->postCompileCallbacks as $cb) { - $parameter = $cb; - $parameter[0] = $this; - call_user_func_array($cb[0], $parameter); + $callbackFunction = $cb[0]; + $parameters = $cb; + $parameters[0] = $this; + $callbackFunction(...$parameters); } // return compiled code return $this->prefixCompiledCode . $this->parser->retvalue . $this->postfixCompiledCode; diff --git a/src/CompilerException.php b/src/CompilerException.php index e3d67b4..60af9db 100644 --- a/src/CompilerException.php +++ b/src/CompilerException.php @@ -16,14 +16,14 @@ class CompilerException extends Exception { * @param int $code The Exception code. * @param string|null $filename The filename where the exception is thrown. * @param int|null $line The line number where the exception is thrown. - * @param Throwable|null $previous The previous exception used for the exception chaining. + * @param \Throwable|null $previous The previous exception used for the exception chaining. */ public function __construct( string $message = "", int $code = 0, ?string $filename = null, ?int $line = null, - Throwable $previous = null + ?\Throwable $previous = null ) { parent::__construct($message, $code, $previous); diff --git a/src/Extension/CallbackWrapper.php b/src/Extension/CallbackWrapper.php index 827dd78..193fc13 100644 --- a/src/Extension/CallbackWrapper.php +++ b/src/Extension/CallbackWrapper.php @@ -26,7 +26,7 @@ class CallbackWrapper { public function handle(...$params) { try { - return call_user_func_array($this->callback, $params); + return ($this->callback)(...$params); } catch (\ArgumentCountError $e) { throw new Exception("Invalid number of arguments to modifier " . $this->modifierName); } diff --git a/src/FunctionHandler/HtmlBase.php b/src/FunctionHandler/HtmlBase.php index f321b84..99f8e6c 100644 --- a/src/FunctionHandler/HtmlBase.php +++ b/src/FunctionHandler/HtmlBase.php @@ -15,7 +15,6 @@ class HtmlBase extends Base { * @param $separator * @param $labels * @param $label_ids - * @param $pos * @param bool $escape * * @return string @@ -31,7 +30,6 @@ class HtmlBase extends Base { $separator, $labels, $label_ids, - $pos, $escape = true ) { @@ -85,7 +83,7 @@ class HtmlBase extends Base { } $_output .= ' 74, 1 => 3 ), array( 0 => 91, 1 => 1 ), array( 0 => 91, 1 => 1 ), + array( 0 => 95, 1 => 3 ), + array( 0 => 95, 1 => 3 ), + array( 0 => 95, 1 => 1 ), + array( 0 => 95, 1 => 1 ), + array( 0 => 95, 1 => 0 ), + array( 0 => 96, 1 => 1 ), + array( 0 => 96, 1 => 1 ), + array( 0 => 96, 1 => 3 ), + array( 0 => 96, 1 => 1 ), + array( 0 => 98, 1 => 3 ), + array( 0 => 98, 1 => 4 ), + array( 0 => 98, 1 => 3 ), + array( 0 => 98, 1 => 4 ), array( 0 => 73, 1 => 1 ), array( 0 => 73, 1 => 1 ), - array( 0 => 73, 1 => 3 ), - array( 0 => 73, 1 => 1 ), - array( 0 => 73, 1 => 3 ), - array( 0 => 73, 1 => 4 ), - array( 0 => 73, 1 => 3 ), - array( 0 => 73, 1 => 4 ), array( 0 => 70, 1 => 2 ), array( 0 => 70, 1 => 2 ), - array( 0 => 96, 1 => 2 ), - array( 0 => 96, 1 => 0 ), - array( 0 => 97, 1 => 2 ), - array( 0 => 97, 1 => 2 ), - array( 0 => 97, 1 => 4 ), - array( 0 => 97, 1 => 2 ), - array( 0 => 97, 1 => 2 ), - array( 0 => 97, 1 => 4 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 5 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 3 ), - array( 0 => 97, 1 => 2 ), + array( 0 => 99, 1 => 2 ), + array( 0 => 99, 1 => 0 ), + array( 0 => 100, 1 => 2 ), + array( 0 => 100, 1 => 2 ), + array( 0 => 100, 1 => 4 ), + array( 0 => 100, 1 => 2 ), + array( 0 => 100, 1 => 2 ), + array( 0 => 100, 1 => 4 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 5 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 3 ), + array( 0 => 100, 1 => 2 ), array( 0 => 80, 1 => 1 ), array( 0 => 80, 1 => 1 ), array( 0 => 80, 1 => 2 ), - array( 0 => 98, 1 => 1 ), - array( 0 => 98, 1 => 1 ), - array( 0 => 98, 1 => 3 ), - array( 0 => 95, 1 => 2 ), - array( 0 => 99, 1 => 1 ), - array( 0 => 99, 1 => 2 ), - array( 0 => 100, 1 => 3 ), - array( 0 => 100, 1 => 3 ), - array( 0 => 100, 1 => 5 ), - array( 0 => 100, 1 => 6 ), - array( 0 => 100, 1 => 2 ), - array( 0 => 90, 1 => 4 ), - array( 0 => 101, 1 => 4 ), - array( 0 => 101, 1 => 4 ), - array( 0 => 102, 1 => 3 ), + array( 0 => 101, 1 => 1 ), + array( 0 => 101, 1 => 1 ), + array( 0 => 101, 1 => 3 ), + array( 0 => 97, 1 => 2 ), array( 0 => 102, 1 => 1 ), - array( 0 => 102, 1 => 0 ), + array( 0 => 102, 1 => 2 ), + array( 0 => 103, 1 => 3 ), + array( 0 => 103, 1 => 3 ), + array( 0 => 103, 1 => 5 ), + array( 0 => 103, 1 => 6 ), + array( 0 => 103, 1 => 2 ), + array( 0 => 90, 1 => 4 ), + array( 0 => 104, 1 => 4 ), + array( 0 => 104, 1 => 4 ), + array( 0 => 105, 1 => 3 ), + array( 0 => 105, 1 => 1 ), + array( 0 => 105, 1 => 0 ), array( 0 => 76, 1 => 3 ), array( 0 => 76, 1 => 2 ), - array( 0 => 103, 1 => 3 ), - array( 0 => 103, 1 => 2 ), + array( 0 => 106, 1 => 3 ), + array( 0 => 106, 1 => 2 ), array( 0 => 81, 1 => 2 ), array( 0 => 81, 1 => 0 ), - array( 0 => 104, 1 => 2 ), - array( 0 => 104, 1 => 3 ), - array( 0 => 104, 1 => 2 ), + array( 0 => 107, 1 => 2 ), + array( 0 => 107, 1 => 3 ), + array( 0 => 107, 1 => 2 ), array( 0 => 93, 1 => 1 ), array( 0 => 93, 1 => 2 ), array( 0 => 93, 1 => 1 ), @@ -1916,23 +1975,23 @@ public static $yy_action = array( array( 0 => 87, 1 => 1 ), array( 0 => 94, 1 => 3 ), array( 0 => 94, 1 => 3 ), - array( 0 => 105, 1 => 1 ), - array( 0 => 105, 1 => 3 ), - array( 0 => 105, 1 => 0 ), - array( 0 => 106, 1 => 3 ), - array( 0 => 106, 1 => 3 ), - array( 0 => 106, 1 => 1 ), + array( 0 => 108, 1 => 1 ), + array( 0 => 108, 1 => 3 ), + array( 0 => 108, 1 => 0 ), + array( 0 => 109, 1 => 3 ), + array( 0 => 109, 1 => 3 ), + array( 0 => 109, 1 => 1 ), array( 0 => 92, 1 => 2 ), array( 0 => 92, 1 => 3 ), - array( 0 => 107, 1 => 2 ), - array( 0 => 107, 1 => 1 ), - array( 0 => 108, 1 => 3 ), - array( 0 => 108, 1 => 3 ), - array( 0 => 108, 1 => 1 ), - array( 0 => 108, 1 => 3 ), - array( 0 => 108, 1 => 3 ), - array( 0 => 108, 1 => 1 ), - array( 0 => 108, 1 => 1 ), + array( 0 => 110, 1 => 2 ), + array( 0 => 110, 1 => 1 ), + array( 0 => 111, 1 => 3 ), + array( 0 => 111, 1 => 3 ), + array( 0 => 111, 1 => 1 ), + array( 0 => 111, 1 => 3 ), + array( 0 => 111, 1 => 3 ), + array( 0 => 111, 1 => 1 ), + array( 0 => 111, 1 => 1 ), ); public static $yyReduceMap = array( @@ -1961,9 +2020,9 @@ public static $yy_action = array( 103 => 6, 104 => 6, 106 => 6, - 111 => 6, - 175 => 6, - 180 => 6, + 122 => 6, + 182 => 6, + 187 => 6, 7 => 7, 8 => 8, 9 => 9, @@ -2003,18 +2062,21 @@ public static $yy_action = array( 48 => 48, 49 => 49, 58 => 49, - 153 => 49, - 157 => 49, - 161 => 49, - 163 => 49, + 110 => 49, + 111 => 49, + 160 => 49, + 164 => 49, + 168 => 49, + 170 => 49, 50 => 50, - 154 => 50, - 160 => 50, + 112 => 50, + 161 => 50, + 167 => 50, 51 => 51, 52 => 52, 53 => 52, 54 => 54, - 138 => 54, + 145 => 54, 57 => 57, 59 => 59, 60 => 60, @@ -2040,7 +2102,7 @@ public static $yy_action = array( 84 => 84, 86 => 84, 87 => 84, - 118 => 84, + 125 => 84, 85 => 85, 90 => 90, 91 => 91, @@ -2055,42 +2117,40 @@ public static $yy_action = array( 105 => 105, 107 => 107, 108 => 108, - 109 => 109, - 110 => 110, - 112 => 112, + 109 => 108, + 159 => 108, 113 => 113, 114 => 114, 115 => 115, 116 => 116, 117 => 117, + 118 => 118, 119 => 119, - 177 => 119, 120 => 120, 121 => 121, - 122 => 122, 123 => 123, 124 => 124, - 125 => 125, - 133 => 125, 126 => 126, + 184 => 126, 127 => 127, 128 => 128, - 129 => 128, - 131 => 128, - 132 => 128, + 129 => 129, 130 => 130, + 131 => 131, + 132 => 132, + 140 => 132, + 133 => 133, 134 => 134, 135 => 135, - 136 => 136, - 181 => 136, + 136 => 135, + 138 => 135, + 139 => 135, 137 => 137, - 139 => 139, - 140 => 140, 141 => 141, 142 => 142, 143 => 143, + 188 => 143, 144 => 144, - 145 => 145, 146 => 146, 147 => 147, 148 => 148, @@ -2098,35 +2158,41 @@ public static $yy_action = array( 150 => 150, 151 => 151, 152 => 152, + 153 => 153, + 154 => 154, 155 => 155, 156 => 156, + 157 => 157, 158 => 158, - 159 => 159, 162 => 162, - 164 => 164, + 163 => 163, 165 => 165, 166 => 166, - 167 => 167, - 168 => 168, 169 => 169, - 170 => 170, 171 => 171, 172 => 172, 173 => 173, - 174 => 173, + 174 => 174, + 175 => 175, 176 => 176, + 177 => 177, 178 => 178, 179 => 179, - 182 => 182, + 180 => 180, + 181 => 180, 183 => 183, - 184 => 184, 185 => 185, - 188 => 185, 186 => 186, - 189 => 186, - 187 => 187, + 189 => 189, 190 => 190, 191 => 191, + 192 => 192, + 195 => 192, + 193 => 193, + 196 => 193, + 194 => 194, + 197 => 197, + 198 => 198, ); // line 245 "src/Parser/TemplateParser.y" public function yy_r0(){ @@ -2187,9 +2253,10 @@ public static $yy_action = array( $attributes[] = 'nocache'; $var = $match[1]; } - $this->_retvalue = $this->compiler->compilePrintExpression($this->compiler->compileVariable('\''.$var.'\''), $attributes); + $this->compiler->triggerTagNoCache($var); + $this->_retvalue = $this->compiler->compilePrintExpression('$_smarty_tpl->getValue(\''.$var.'\')', $attributes); } -// line 313 "src/Parser/TemplateParser.y" +// line 314 "src/Parser/TemplateParser.y" public function yy_r12(){ $tag = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength())); if ($tag == 'strip') { @@ -2210,7 +2277,7 @@ public static $yy_action = array( } } } -// line 334 "src/Parser/TemplateParser.y" +// line 335 "src/Parser/TemplateParser.y" public function yy_r13(){ $j = strrpos($this->yystack[$this->yyidx + 0]->minor,'.'); if ($this->yystack[$this->yyidx + 0]->minor[$j+1] == 'c') { @@ -2221,35 +2288,35 @@ public static $yy_action = array( $this->_retvalue = $this->compiler->compileParentBlock(); } } -// line 345 "src/Parser/TemplateParser.y" +// line 346 "src/Parser/TemplateParser.y" public function yy_r14(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } -// line 349 "src/Parser/TemplateParser.y" +// line 350 "src/Parser/TemplateParser.y" public function yy_r15(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } -// line 353 "src/Parser/TemplateParser.y" +// line 354 "src/Parser/TemplateParser.y" public function yy_r16(){ $this->_retvalue = $this->compiler->compilePrintExpression($this->yystack[$this->yyidx + 0]->minor[0], $this->yystack[$this->yyidx + 0]->minor[1]); } -// line 362 "src/Parser/TemplateParser.y" +// line 363 "src/Parser/TemplateParser.y" public function yy_r17(){ $this->_retvalue = $this->compiler->compileTag('assign',array_merge(array(array('value'=>$this->yystack[$this->yyidx + 0]->minor[0]),array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'')),$this->yystack[$this->yyidx + 0]->minor[1])); } -// line 366 "src/Parser/TemplateParser.y" +// line 367 "src/Parser/TemplateParser.y" public function yy_r18(){ $this->_retvalue = $this->compiler->compileTag('assign',array_merge(array(array('value'=>$this->yystack[$this->yyidx + 0]->minor[0]),array('var'=>$this->yystack[$this->yyidx + -1]->minor['var'])),$this->yystack[$this->yyidx + 0]->minor[1]),array('smarty_internal_index'=>$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'])); } -// line 370 "src/Parser/TemplateParser.y" +// line 371 "src/Parser/TemplateParser.y" public function yy_r19(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -// line 374 "src/Parser/TemplateParser.y" +// line 375 "src/Parser/TemplateParser.y" public function yy_r20(){ $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); } -// line 389 "src/Parser/TemplateParser.y" +// line 390 "src/Parser/TemplateParser.y" public function yy_r24(){ if (defined($this->yystack[$this->yyidx + -1]->minor)) { if ($this->security) { @@ -2260,7 +2327,7 @@ public static $yy_action = array( $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); } } -// line 399 "src/Parser/TemplateParser.y" +// line 400 "src/Parser/TemplateParser.y" public function yy_r25(){ if (defined($this->yystack[$this->yyidx + 0]->minor)) { if ($this->security) { @@ -2271,7 +2338,7 @@ public static $yy_action = array( $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + 0]->minor,array()); } } -// line 412 "src/Parser/TemplateParser.y" +// line 413 "src/Parser/TemplateParser.y" public function yy_r26(){ if (defined($this->yystack[$this->yyidx + -2]->minor)) { if ($this->security) { @@ -2282,66 +2349,66 @@ public static $yy_action = array( $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -2]->minor,$this->yystack[$this->yyidx + 0]->minor, array('modifierlist'=>$this->yystack[$this->yyidx + -1]->minor)); } } -// line 424 "src/Parser/TemplateParser.y" +// line 425 "src/Parser/TemplateParser.y" public function yy_r27(){ $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -3]->minor,$this->yystack[$this->yyidx + 0]->minor,array('object_method'=>$this->yystack[$this->yyidx + -1]->minor)); } -// line 429 "src/Parser/TemplateParser.y" +// line 430 "src/Parser/TemplateParser.y" public function yy_r28(){ $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -4]->minor,$this->yystack[$this->yyidx + 0]->minor,array('modifierlist'=>$this->yystack[$this->yyidx + -1]->minor, 'object_method'=>$this->yystack[$this->yyidx + -2]->minor)); } -// line 434 "src/Parser/TemplateParser.y" +// line 435 "src/Parser/TemplateParser.y" public function yy_r29(){ $tag = trim(substr($this->yystack[$this->yyidx + -1]->minor,$this->compiler->getLdelLength())); $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>$this->yystack[$this->yyidx + 0]->minor)); } -// line 439 "src/Parser/TemplateParser.y" +// line 440 "src/Parser/TemplateParser.y" public function yy_r30(){ $tag = trim(substr($this->yystack[$this->yyidx + -2]->minor,$this->compiler->getLdelLength())); $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,$this->yystack[$this->yyidx + 0]->minor,array('if condition'=>$this->yystack[$this->yyidx + -1]->minor)); } -// line 444 "src/Parser/TemplateParser.y" +// line 445 "src/Parser/TemplateParser.y" public function yy_r31(){ $tag = trim(substr($this->yystack[$this->yyidx + -1]->minor,$this->compiler->getLdelLength())); $this->_retvalue = $this->compiler->compileTag(($tag === 'else if')? 'elseif' : $tag,array(),array('if condition'=>$this->yystack[$this->yyidx + 0]->minor)); } -// line 455 "src/Parser/TemplateParser.y" +// line 456 "src/Parser/TemplateParser.y" public function yy_r33(){ $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -6]->minor),array('ifexp'=>$this->yystack[$this->yyidx + -4]->minor),array('var'=>$this->yystack[$this->yyidx + -2]->minor),array('step'=>$this->yystack[$this->yyidx + -1]->minor))),1); } -// line 459 "src/Parser/TemplateParser.y" +// line 460 "src/Parser/TemplateParser.y" public function yy_r34(){ $this->_retvalue = '='.$this->yystack[$this->yyidx + 0]->minor; } -// line 467 "src/Parser/TemplateParser.y" +// line 468 "src/Parser/TemplateParser.y" public function yy_r36(){ $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -3]->minor),array('to'=>$this->yystack[$this->yyidx + -1]->minor))),0); } -// line 471 "src/Parser/TemplateParser.y" +// line 472 "src/Parser/TemplateParser.y" public function yy_r37(){ $this->_retvalue = $this->compiler->compileTag('for',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('start'=>$this->yystack[$this->yyidx + -5]->minor),array('to'=>$this->yystack[$this->yyidx + -3]->minor),array('step'=>$this->yystack[$this->yyidx + -1]->minor))),0); } -// line 476 "src/Parser/TemplateParser.y" +// line 477 "src/Parser/TemplateParser.y" public function yy_r38(){ $this->_retvalue = $this->compiler->compileTag('foreach',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('from'=>$this->yystack[$this->yyidx + -3]->minor),array('item'=>$this->yystack[$this->yyidx + -1]->minor)))); } -// line 480 "src/Parser/TemplateParser.y" +// line 481 "src/Parser/TemplateParser.y" public function yy_r39(){ $this->_retvalue = $this->compiler->compileTag('foreach',array_merge($this->yystack[$this->yyidx + 0]->minor,array(array('from'=>$this->yystack[$this->yyidx + -5]->minor),array('item'=>$this->yystack[$this->yyidx + -1]->minor),array('key'=>$this->yystack[$this->yyidx + -3]->minor)))); } -// line 483 "src/Parser/TemplateParser.y" +// line 484 "src/Parser/TemplateParser.y" public function yy_r40(){ $this->_retvalue = $this->compiler->compileTag('foreach',$this->yystack[$this->yyidx + 0]->minor); } -// line 488 "src/Parser/TemplateParser.y" +// line 489 "src/Parser/TemplateParser.y" public function yy_r41(){ $this->_retvalue = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array(array_merge(array($this->yystack[$this->yyidx + -1]->minor),$this->yystack[$this->yyidx + 0]->minor)))); } -// line 492 "src/Parser/TemplateParser.y" +// line 493 "src/Parser/TemplateParser.y" public function yy_r42(){ $this->_retvalue = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array_merge(array(array_merge(array($this->yystack[$this->yyidx + -2]->minor),$this->yystack[$this->yyidx + -1]->minor)),$this->yystack[$this->yyidx + 0]->minor))); } -// line 498 "src/Parser/TemplateParser.y" +// line 499 "src/Parser/TemplateParser.y" public function yy_r43(){ $tag = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' /'); if ($tag === 'strip') { @@ -2351,36 +2418,36 @@ public static $yy_action = array( $this->_retvalue = $this->compiler->compileTag($tag.'close',array()); } } -// line 507 "src/Parser/TemplateParser.y" +// line 508 "src/Parser/TemplateParser.y" public function yy_r44(){ $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + 0]->minor.'close',array()); } -// line 511 "src/Parser/TemplateParser.y" +// line 512 "src/Parser/TemplateParser.y" public function yy_r45(){ $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -1]->minor.'close',array(),array('modifier_list'=>$this->yystack[$this->yyidx + 0]->minor)); } -// line 516 "src/Parser/TemplateParser.y" +// line 517 "src/Parser/TemplateParser.y" public function yy_r46(){ $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -2]->minor.'close',array(),array('object_method'=>$this->yystack[$this->yyidx + 0]->minor)); } -// line 520 "src/Parser/TemplateParser.y" +// line 521 "src/Parser/TemplateParser.y" public function yy_r47(){ $this->_retvalue = $this->compiler->compileTag($this->yystack[$this->yyidx + -3]->minor.'close',array(),array('object_method'=>$this->yystack[$this->yyidx + -1]->minor, 'modifier_list'=>$this->yystack[$this->yyidx + 0]->minor)); } -// line 528 "src/Parser/TemplateParser.y" +// line 529 "src/Parser/TemplateParser.y" public function yy_r48(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; $this->_retvalue[] = $this->yystack[$this->yyidx + 0]->minor; } -// line 534 "src/Parser/TemplateParser.y" +// line 535 "src/Parser/TemplateParser.y" public function yy_r49(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); } -// line 539 "src/Parser/TemplateParser.y" +// line 540 "src/Parser/TemplateParser.y" public function yy_r50(){ $this->_retvalue = array(); } -// line 544 "src/Parser/TemplateParser.y" +// line 545 "src/Parser/TemplateParser.y" public function yy_r51(){ if (defined($this->yystack[$this->yyidx + 0]->minor)) { if ($this->security) { @@ -2391,64 +2458,64 @@ public static $yy_action = array( $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>'\''.$this->yystack[$this->yyidx + 0]->minor.'\''); } } -// line 555 "src/Parser/TemplateParser.y" +// line 556 "src/Parser/TemplateParser.y" public function yy_r52(){ $this->_retvalue = array(trim($this->yystack[$this->yyidx + -1]->minor," =\n\r\t")=>$this->yystack[$this->yyidx + 0]->minor); } -// line 563 "src/Parser/TemplateParser.y" +// line 564 "src/Parser/TemplateParser.y" public function yy_r54(){ $this->_retvalue = '\''.$this->yystack[$this->yyidx + 0]->minor.'\''; } -// line 575 "src/Parser/TemplateParser.y" +// line 576 "src/Parser/TemplateParser.y" public function yy_r57(){ $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor=>$this->yystack[$this->yyidx + 0]->minor); } -// line 588 "src/Parser/TemplateParser.y" +// line 589 "src/Parser/TemplateParser.y" public function yy_r59(){ $this->yystack[$this->yyidx + -2]->minor[]=$this->yystack[$this->yyidx + 0]->minor; $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } -// line 593 "src/Parser/TemplateParser.y" +// line 594 "src/Parser/TemplateParser.y" public function yy_r60(){ $this->_retvalue = array('var' => '\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\'', 'value'=>$this->yystack[$this->yyidx + 0]->minor); } -// line 600 "src/Parser/TemplateParser.y" +// line 601 "src/Parser/TemplateParser.y" public function yy_r62(){ $this->_retvalue = array('var' => $this->yystack[$this->yyidx + -2]->minor, 'value'=>$this->yystack[$this->yyidx + 0]->minor); } -// line 604 "src/Parser/TemplateParser.y" +// line 605 "src/Parser/TemplateParser.y" public function yy_r63(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } -// line 629 "src/Parser/TemplateParser.y" +// line 630 "src/Parser/TemplateParser.y" public function yy_r67(){ $this->_retvalue = '$_smarty_tpl->getVariable(\''. substr($this->yystack[$this->yyidx + 0]->minor,1) .'\')->preIncDec(\'' . $this->yystack[$this->yyidx + -1]->minor . '\')'; } -// line 634 "src/Parser/TemplateParser.y" +// line 635 "src/Parser/TemplateParser.y" public function yy_r68(){ $this->_retvalue = '$_smarty_tpl->getVariable(\''. substr($this->yystack[$this->yyidx + -1]->minor,1) .'\')->postIncDec(\'' . $this->yystack[$this->yyidx + 0]->minor . '\')'; } -// line 639 "src/Parser/TemplateParser.y" +// line 640 "src/Parser/TemplateParser.y" public function yy_r69(){ $this->_retvalue = '$_smarty_tpl->getStreamVariable(\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'://' . $this->yystack[$this->yyidx + 0]->minor . '\')'; } -// line 644 "src/Parser/TemplateParser.y" +// line 645 "src/Parser/TemplateParser.y" public function yy_r70(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor . trim($this->yystack[$this->yyidx + -1]->minor) . $this->yystack[$this->yyidx + 0]->minor; } -// line 654 "src/Parser/TemplateParser.y" +// line 655 "src/Parser/TemplateParser.y" public function yy_r72(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor['pre']. $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor['op'].$this->yystack[$this->yyidx + 0]->minor .')'; } -// line 658 "src/Parser/TemplateParser.y" +// line 659 "src/Parser/TemplateParser.y" public function yy_r73(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } -// line 662 "src/Parser/TemplateParser.y" +// line 663 "src/Parser/TemplateParser.y" public function yy_r74(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor . $this->yystack[$this->yyidx + -1]->minor . ')'; } -// line 666 "src/Parser/TemplateParser.y" +// line 667 "src/Parser/TemplateParser.y" public function yy_r75(){ static $isin = [ 'isin' => 'in_array(', @@ -2457,51 +2524,52 @@ public static $yy_action = array( $op = strtolower(str_replace(' ', '', $this->yystack[$this->yyidx + 0]->minor)); $this->_retvalue = $isin[$op]; } -// line 675 "src/Parser/TemplateParser.y" +// line 676 "src/Parser/TemplateParser.y" public function yy_r76(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor . $this->yystack[$this->yyidx + -2]->minor.','.$this->yystack[$this->yyidx + 0]->minor.')'; } -// line 679 "src/Parser/TemplateParser.y" +// line 680 "src/Parser/TemplateParser.y" public function yy_r77(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor . $this->yystack[$this->yyidx + -2]->minor.',(array)'.$this->yystack[$this->yyidx + 0]->minor.')'; } -// line 684 "src/Parser/TemplateParser.y" +// line 685 "src/Parser/TemplateParser.y" public function yy_r78(){ $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor.' ?? '.$this->yystack[$this->yyidx + 0]->minor; } -// line 691 "src/Parser/TemplateParser.y" +// line 692 "src/Parser/TemplateParser.y" public function yy_r79(){ - $this->_retvalue = $this->yystack[$this->yyidx + -4]->minor.' ? '. $this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\'') . ' : '.$this->yystack[$this->yyidx + 0]->minor; + $this->compiler->triggerTagNoCache(substr($this->yystack[$this->yyidx + -2]->minor,1)); + $this->_retvalue = $this->yystack[$this->yyidx + -4]->minor.' ? $_smarty_tpl->getValue(\''.substr($this->yystack[$this->yyidx + -2]->minor,1).'\') : '.$this->yystack[$this->yyidx + 0]->minor; } -// line 695 "src/Parser/TemplateParser.y" +// line 697 "src/Parser/TemplateParser.y" public function yy_r80(){ $this->_retvalue = $this->yystack[$this->yyidx + -4]->minor.' ? '.$this->yystack[$this->yyidx + -2]->minor.' : '.$this->yystack[$this->yyidx + 0]->minor; } -// line 704 "src/Parser/TemplateParser.y" +// line 706 "src/Parser/TemplateParser.y" public function yy_r82(){ $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor.' ?: '.$this->yystack[$this->yyidx + 0]->minor; } -// line 714 "src/Parser/TemplateParser.y" +// line 716 "src/Parser/TemplateParser.y" public function yy_r84(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } -// line 719 "src/Parser/TemplateParser.y" +// line 721 "src/Parser/TemplateParser.y" public function yy_r85(){ $this->_retvalue = '!'.$this->yystack[$this->yyidx + 0]->minor; } -// line 740 "src/Parser/TemplateParser.y" +// line 742 "src/Parser/TemplateParser.y" public function yy_r90(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'.'.$this->yystack[$this->yyidx + 0]->minor; } -// line 744 "src/Parser/TemplateParser.y" +// line 746 "src/Parser/TemplateParser.y" public function yy_r91(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.'.'; } -// line 748 "src/Parser/TemplateParser.y" +// line 750 "src/Parser/TemplateParser.y" public function yy_r92(){ $this->_retvalue = '.'.$this->yystack[$this->yyidx + 0]->minor; } -// line 753 "src/Parser/TemplateParser.y" +// line 755 "src/Parser/TemplateParser.y" public function yy_r93(){ if (defined($this->yystack[$this->yyidx + 0]->minor)) { if ($this->security) { @@ -2512,15 +2580,15 @@ public static $yy_action = array( $this->_retvalue = '\''.$this->yystack[$this->yyidx + 0]->minor.'\''; } } -// line 770 "src/Parser/TemplateParser.y" +// line 772 "src/Parser/TemplateParser.y" public function yy_r95(){ $this->_retvalue = '('. $this->yystack[$this->yyidx + -1]->minor .')'; } -// line 774 "src/Parser/TemplateParser.y" +// line 776 "src/Parser/TemplateParser.y" public function yy_r96(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } -// line 792 "src/Parser/TemplateParser.y" +// line 794 "src/Parser/TemplateParser.y" public function yy_r100(){ if ($this->security && $this->security->static_classes !== array()) { $this->compiler->trigger_template_error('dynamic static class not allowed by security setting'); @@ -2529,22 +2597,23 @@ public static $yy_action = array( if ($this->yystack[$this->yyidx + -2]->minor['var'] === '\'smarty\'') { $this->compiler->appendPrefixCode("compile(array(),$this->compiler,$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index']).';?>'); } else { - $this->compiler->appendPrefixCode("compiler->compileVariable($this->yystack[$this->yyidx + -2]->minor['var']).$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index'].';?>'); + $this->compiler->triggerTagNoCache($this->yystack[$this->yyidx + -2]->minor['var']); + $this->compiler->appendPrefixCode("getValue(" . $this->yystack[$this->yyidx + -2]->minor['var'] . ')'.$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index'].';?>'); } $this->_retvalue = $prefixVar .'::'.$this->yystack[$this->yyidx + 0]->minor[0].$this->yystack[$this->yyidx + 0]->minor[1]; } -// line 806 "src/Parser/TemplateParser.y" +// line 809 "src/Parser/TemplateParser.y" public function yy_r101(){ $prefixVar = $this->compiler->getNewPrefixVariable(); $tmp = $this->compiler->appendCode('', (string) $this->yystack[$this->yyidx + 0]->minor); $this->compiler->appendPrefixCode($this->compiler->appendCode($tmp, "")); $this->_retvalue = $prefixVar; } -// line 813 "src/Parser/TemplateParser.y" +// line 816 "src/Parser/TemplateParser.y" public function yy_r102(){ $this->_retvalue = $this->compiler->compileModifier($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } -// line 826 "src/Parser/TemplateParser.y" +// line 829 "src/Parser/TemplateParser.y" public function yy_r105(){ if (!in_array(strtolower($this->yystack[$this->yyidx + -2]->minor), array('self', 'parent')) && (!$this->security || $this->security->isTrustedStaticClassAccess($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->compiler))) { if (isset($this->smarty->registered_classes[$this->yystack[$this->yyidx + -2]->minor])) { @@ -2556,241 +2625,292 @@ public static $yy_action = array( $this->compiler->trigger_template_error ('static class \''.$this->yystack[$this->yyidx + -2]->minor.'\' is undefined or not allowed by security setting'); } } -// line 845 "src/Parser/TemplateParser.y" +// line 848 "src/Parser/TemplateParser.y" public function yy_r107(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } // line 856 "src/Parser/TemplateParser.y" public function yy_r108(){ - $this->_retvalue = $this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''); + $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array($this->yystack[$this->yyidx + 0]->minor)); } -// line 859 "src/Parser/TemplateParser.y" - public function yy_r109(){ +// line 883 "src/Parser/TemplateParser.y" + public function yy_r113(){ + $this->compiler->triggerTagNoCache(substr($this->yystack[$this->yyidx + 0]->minor,1)); + $this->_retvalue = array('$_smarty_tpl->hasVariable(\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\')','$_smarty_tpl->getValue(\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\')'); + } +// line 887 "src/Parser/TemplateParser.y" + public function yy_r114(){ if ($this->yystack[$this->yyidx + 0]->minor['var'] === '\'smarty\'') { $smarty_var = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']); - $this->_retvalue = $smarty_var; + $this->_retvalue = array('true', $smarty_var); } else { // used for array reset,next,prev,end,current $this->last_variable = $this->yystack[$this->yyidx + 0]->minor['var']; $this->last_index = $this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']; - $this->_retvalue = $this->compiler->compileVariable($this->yystack[$this->yyidx + 0]->minor['var']).$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']; + $this->compiler->triggerTagNoCache($this->yystack[$this->yyidx + 0]->minor['var']); + $this->_retvalue = array('true', '$_smarty_tpl->getValue(' . $this->yystack[$this->yyidx + 0]->minor['var'] . ')'.$this->yystack[$this->yyidx + 0]->minor['smarty_internal_index']); } } -// line 872 "src/Parser/TemplateParser.y" - public function yy_r110(){ - $this->_retvalue = '$_smarty_tpl->getVariable('. $this->yystack[$this->yyidx + -2]->minor .')->'.$this->yystack[$this->yyidx + 0]->minor; - } -// line 882 "src/Parser/TemplateParser.y" - public function yy_r112(){ - $this->_retvalue = $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -1]->minor . '\''); - } -// line 886 "src/Parser/TemplateParser.y" - public function yy_r113(){ - $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -2]->minor . '\'') . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' :null)'; - } -// line 890 "src/Parser/TemplateParser.y" - public function yy_r114(){ - $this->_retvalue = $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -1]->minor); - } -// line 894 "src/Parser/TemplateParser.y" - public function yy_r115(){ - $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -2]->minor) . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' : null)'; - } -// line 898 "src/Parser/TemplateParser.y" - public function yy_r116(){ - $this->_retvalue = array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'', 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); - } // line 901 "src/Parser/TemplateParser.y" + public function yy_r115(){ + $this->_retvalue = array('true', '$_smarty_tpl->getVariable('. $this->yystack[$this->yyidx + -2]->minor .')->'.$this->yystack[$this->yyidx + 0]->minor); + } +// line 906 "src/Parser/TemplateParser.y" + public function yy_r116(){ + $this->_retvalue = array('true', $this->yystack[$this->yyidx + 0]->minor); + } +// line 911 "src/Parser/TemplateParser.y" public function yy_r117(){ - $this->_retvalue = array('var'=>$this->yystack[$this->yyidx + -1]->minor, 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -1]->minor . '\''); } -// line 914 "src/Parser/TemplateParser.y" +// line 915 "src/Parser/TemplateParser.y" + public function yy_r118(){ + $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . $this->yystack[$this->yyidx + -2]->minor . '\'') . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' :null)'; + } +// line 919 "src/Parser/TemplateParser.y" public function yy_r119(){ - return; - } -// line 920 "src/Parser/TemplateParser.y" - public function yy_r120(){ - $this->_retvalue = '['.$this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\'').']'; + $this->_retvalue = $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -1]->minor); } // line 923 "src/Parser/TemplateParser.y" - public function yy_r121(){ - $this->_retvalue = '['.$this->compiler->compileVariable($this->yystack[$this->yyidx + 0]->minor).']'; + public function yy_r120(){ + $this->_retvalue = '(is_array($tmp = ' . $this->compiler->compileConfigVariable($this->yystack[$this->yyidx + -2]->minor) . ') ? $tmp'.$this->yystack[$this->yyidx + 0]->minor.' : null)'; } // line 927 "src/Parser/TemplateParser.y" - public function yy_r122(){ - $this->_retvalue = '['.$this->compiler->compileVariable($this->yystack[$this->yyidx + -2]->minor).'->'.$this->yystack[$this->yyidx + 0]->minor.']'; - } -// line 931 "src/Parser/TemplateParser.y" - public function yy_r123(){ - $this->_retvalue = '[\''. $this->yystack[$this->yyidx + 0]->minor .'\']'; + public function yy_r121(){ + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor[1]; } // line 935 "src/Parser/TemplateParser.y" + public function yy_r123(){ + $this->_retvalue = array('var'=>'\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'', 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 938 "src/Parser/TemplateParser.y" public function yy_r124(){ + $this->_retvalue = array('var'=>$this->yystack[$this->yyidx + -1]->minor, 'smarty_internal_index'=>$this->yystack[$this->yyidx + 0]->minor); + } +// line 951 "src/Parser/TemplateParser.y" + public function yy_r126(){ + return; + } +// line 957 "src/Parser/TemplateParser.y" + public function yy_r127(){ + $this->compiler->triggerTagNoCache(substr($this->yystack[$this->yyidx + 0]->minor,1)); + $this->_retvalue = '[$_smarty_tpl->getValue(\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\')]'; + } +// line 961 "src/Parser/TemplateParser.y" + public function yy_r128(){ + $this->compiler->triggerTagNoCache($this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = '[$_smarty_tpl->getValue(' . $this->yystack[$this->yyidx + 0]->minor . ')]'; + } +// line 966 "src/Parser/TemplateParser.y" + public function yy_r129(){ + $this->compiler->triggerTagNoCache($this->yystack[$this->yyidx + -2]->minor); + $this->_retvalue = '[$_smarty_tpl->getValue(' . $this->yystack[$this->yyidx + -2]->minor . ')->'.$this->yystack[$this->yyidx + 0]->minor.']'; + } +// line 971 "src/Parser/TemplateParser.y" + public function yy_r130(){ + $this->_retvalue = '[\''. $this->yystack[$this->yyidx + 0]->minor .'\']'; + } +// line 975 "src/Parser/TemplateParser.y" + public function yy_r131(){ $this->_retvalue = '['. $this->yystack[$this->yyidx + 0]->minor .']'; } -// line 940 "src/Parser/TemplateParser.y" - public function yy_r125(){ +// line 980 "src/Parser/TemplateParser.y" + public function yy_r132(){ $this->_retvalue = '['. $this->yystack[$this->yyidx + -1]->minor .']'; } -// line 945 "src/Parser/TemplateParser.y" - public function yy_r126(){ +// line 985 "src/Parser/TemplateParser.y" + public function yy_r133(){ $this->_retvalue = '['.(new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,'[\'section\'][\''.$this->yystack[$this->yyidx + -1]->minor.'\'][\'index\']').']'; } -// line 949 "src/Parser/TemplateParser.y" - public function yy_r127(){ +// line 989 "src/Parser/TemplateParser.y" + public function yy_r134(){ $this->_retvalue = '['.(new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,'[\'section\'][\''.$this->yystack[$this->yyidx + -3]->minor.'\'][\''.$this->yystack[$this->yyidx + -1]->minor.'\']').']'; } -// line 952 "src/Parser/TemplateParser.y" - public function yy_r128(){ +// line 992 "src/Parser/TemplateParser.y" + public function yy_r135(){ $this->_retvalue = '['.$this->yystack[$this->yyidx + -1]->minor.']'; } -// line 958 "src/Parser/TemplateParser.y" - public function yy_r130(){ - $this->_retvalue = '['.$this->compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\'').']'; +// line 998 "src/Parser/TemplateParser.y" + public function yy_r137(){ + $this->compiler->triggerTagNoCache(substr($this->yystack[$this->yyidx + -1]->minor,1)); + $this->_retvalue = '[$_smarty_tpl->getValue(\''.substr($this->yystack[$this->yyidx + -1]->minor,1).'\')]'; } -// line 974 "src/Parser/TemplateParser.y" - public function yy_r134(){ +// line 1015 "src/Parser/TemplateParser.y" + public function yy_r141(){ $this->_retvalue = '[]'; } -// line 984 "src/Parser/TemplateParser.y" - public function yy_r135(){ +// line 1025 "src/Parser/TemplateParser.y" + public function yy_r142(){ $this->_retvalue = '\''.substr($this->yystack[$this->yyidx + 0]->minor,1).'\''; } -// line 988 "src/Parser/TemplateParser.y" - public function yy_r136(){ +// line 1029 "src/Parser/TemplateParser.y" + public function yy_r143(){ $this->_retvalue = '\'\''; } -// line 993 "src/Parser/TemplateParser.y" - public function yy_r137(){ +// line 1034 "src/Parser/TemplateParser.y" + public function yy_r144(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.'.'.$this->yystack[$this->yyidx + 0]->minor; } -// line 1001 "src/Parser/TemplateParser.y" - public function yy_r139(){ +// line 1042 "src/Parser/TemplateParser.y" + public function yy_r146(){ $var = trim(substr($this->yystack[$this->yyidx + 0]->minor, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); - $this->_retvalue = $this->compiler->compileVariable('\''.$var.'\''); + $this->compiler->triggerTagNoCache($var); + $this->_retvalue = '$_smarty_tpl->getValue(\''.$var.'\')'; } -// line 1007 "src/Parser/TemplateParser.y" - public function yy_r140(){ +// line 1049 "src/Parser/TemplateParser.y" + public function yy_r147(){ $this->_retvalue = '('.$this->yystack[$this->yyidx + -1]->minor.')'; } -// line 1014 "src/Parser/TemplateParser.y" - public function yy_r141(){ +// line 1056 "src/Parser/TemplateParser.y" + public function yy_r148(){ if ($this->yystack[$this->yyidx + -1]->minor['var'] === '\'smarty\'') { $this->_retvalue = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index']).$this->yystack[$this->yyidx + 0]->minor; } else { - $this->_retvalue = $this->compiler->compileVariable($this->yystack[$this->yyidx + -1]->minor['var']).$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'].$this->yystack[$this->yyidx + 0]->minor; + $this->compiler->triggerTagNoCache($this->yystack[$this->yyidx + -1]->minor['var']); + $this->_retvalue = '$_smarty_tpl->getValue(' . $this->yystack[$this->yyidx + -1]->minor['var'] . ')'.$this->yystack[$this->yyidx + -1]->minor['smarty_internal_index'].$this->yystack[$this->yyidx + 0]->minor; } } -// line 1023 "src/Parser/TemplateParser.y" - public function yy_r142(){ +// line 1066 "src/Parser/TemplateParser.y" + public function yy_r149(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -// line 1028 "src/Parser/TemplateParser.y" - public function yy_r143(){ +// line 1071 "src/Parser/TemplateParser.y" + public function yy_r150(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } -// line 1033 "src/Parser/TemplateParser.y" - public function yy_r144(){ +// line 1076 "src/Parser/TemplateParser.y" + public function yy_r151(){ if ($this->security && substr($this->yystack[$this->yyidx + -1]->minor,0,1) === '_') { $this->compiler->trigger_template_error (self::ERR1); } $this->_retvalue = '->'.$this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor; } -// line 1040 "src/Parser/TemplateParser.y" - public function yy_r145(){ +// line 1083 "src/Parser/TemplateParser.y" + public function yy_r152(){ if ($this->security) { $this->compiler->trigger_template_error (self::ERR2); } - $this->_retvalue = '->{'.$this->compiler->compileVariable($this->yystack[$this->yyidx + -1]->minor).$this->yystack[$this->yyidx + 0]->minor.'}'; + $this->compiler->triggerTagNoCache($this->yystack[$this->yyidx + -1]->minor); + $this->_retvalue = '->{$_smarty_tpl->getValue(' . $this->yystack[$this->yyidx + -1]->minor . ')'.$this->yystack[$this->yyidx + 0]->minor.'}'; } -// line 1047 "src/Parser/TemplateParser.y" - public function yy_r146(){ +// line 1091 "src/Parser/TemplateParser.y" + public function yy_r153(){ if ($this->security) { $this->compiler->trigger_template_error (self::ERR2); } $this->_retvalue = '->{'.$this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + 0]->minor.'}'; } -// line 1054 "src/Parser/TemplateParser.y" - public function yy_r147(){ +// line 1098 "src/Parser/TemplateParser.y" + public function yy_r154(){ if ($this->security) { $this->compiler->trigger_template_error (self::ERR2); } $this->_retvalue = '->{\''.$this->yystack[$this->yyidx + -4]->minor.'\'.'.$this->yystack[$this->yyidx + -2]->minor.$this->yystack[$this->yyidx + 0]->minor.'}'; } -// line 1062 "src/Parser/TemplateParser.y" - public function yy_r148(){ +// line 1106 "src/Parser/TemplateParser.y" + public function yy_r155(){ $this->_retvalue = '->'.$this->yystack[$this->yyidx + 0]->minor; } -// line 1070 "src/Parser/TemplateParser.y" - public function yy_r149(){ - $this->_retvalue = $this->compiler->compileModifierInExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); +// line 1114 "src/Parser/TemplateParser.y" + public function yy_r156(){ + + if ($this->yystack[$this->yyidx + -3]->minor == 'isset') { + $this->_retvalue = '(true'; + if (count($this->yystack[$this->yyidx + -1]->minor) == 0) { + throw new CompilerException("Invalid number of arguments for isset. isset expects at least one parameter."); + } + foreach ($this->yystack[$this->yyidx + -1]->minor as $value) { + if (is_array($value)) { + $this->_retvalue .= ' && (' . $value[0] . ' && null !== (' . $value[1] . ' ?? null))'; + } else { + $this->_retvalue .= ' && (' . $value . ' !== null)'; + } + } + $this->_retvalue .= ')'; + } elseif ($this->yystack[$this->yyidx + -3]->minor == 'empty') { + if (count($this->yystack[$this->yyidx + -1]->minor) != 1) { + throw new CompilerException("Invalid number of arguments for empty. empty expects at exactly one parameter."); + } + if (is_array($this->yystack[$this->yyidx + -1]->minor[0])) { + $this->_retvalue .= '( !' . $this->yystack[$this->yyidx + -1]->minor[0][0] . ' || empty(' . $this->yystack[$this->yyidx + -1]->minor[0][1] . '))'; + } else { + $this->_retvalue = 'false == ' . $this->yystack[$this->yyidx + -1]->minor[0]; + } + } else { + $p = array(); + foreach ($this->yystack[$this->yyidx + -1]->minor as $value) { + if (is_array($value)) { + $p[] = $value[1]; + } else { + $p[] = $value; + } + } + $this->_retvalue = $this->compiler->compileModifierInExpression($this->yystack[$this->yyidx + -3]->minor, $p); } -// line 1078 "src/Parser/TemplateParser.y" - public function yy_r150(){ + } +// line 1155 "src/Parser/TemplateParser.y" + public function yy_r157(){ if ($this->security && substr($this->yystack[$this->yyidx + -3]->minor,0,1) === '_') { $this->compiler->trigger_template_error (self::ERR1); } $this->_retvalue = $this->yystack[$this->yyidx + -3]->minor . '('. implode(',',$this->yystack[$this->yyidx + -1]->minor) .')'; } -// line 1085 "src/Parser/TemplateParser.y" - public function yy_r151(){ +// line 1162 "src/Parser/TemplateParser.y" + public function yy_r158(){ if ($this->security) { $this->compiler->trigger_template_error (self::ERR2); } $prefixVar = $this->compiler->getNewPrefixVariable(); - $this->compiler->appendPrefixCode("compiler->compileVariable('\''.substr($this->yystack[$this->yyidx + -3]->minor,1).'\'').';?>'); + $this->compiler->triggerTagNoCache(substr($this->yystack[$this->yyidx + -3]->minor,1)); + $this->compiler->appendPrefixCode("getValue('".substr($this->yystack[$this->yyidx + -3]->minor,1).'\')'.';?>'); $this->_retvalue = $prefixVar .'('. implode(',',$this->yystack[$this->yyidx + -1]->minor) .')'; } -// line 1096 "src/Parser/TemplateParser.y" - public function yy_r152(){ - $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array($this->yystack[$this->yyidx + 0]->minor)); - } -// line 1113 "src/Parser/TemplateParser.y" - public function yy_r155(){ +// line 1191 "src/Parser/TemplateParser.y" + public function yy_r162(){ $this->_retvalue = array_merge($this->yystack[$this->yyidx + -2]->minor,array(array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor))); } -// line 1117 "src/Parser/TemplateParser.y" - public function yy_r156(){ +// line 1195 "src/Parser/TemplateParser.y" + public function yy_r163(){ $this->_retvalue = array(array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor)); } -// line 1125 "src/Parser/TemplateParser.y" - public function yy_r158(){ +// line 1203 "src/Parser/TemplateParser.y" + public function yy_r165(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); } -// line 1133 "src/Parser/TemplateParser.y" - public function yy_r159(){ +// line 1211 "src/Parser/TemplateParser.y" + public function yy_r166(){ $this->_retvalue = array_merge($this->yystack[$this->yyidx + -1]->minor,$this->yystack[$this->yyidx + 0]->minor); } -// line 1146 "src/Parser/TemplateParser.y" - public function yy_r162(){ +// line 1224 "src/Parser/TemplateParser.y" + public function yy_r169(){ $this->_retvalue = array(trim($this->yystack[$this->yyidx + -1]->minor).$this->yystack[$this->yyidx + 0]->minor); } -// line 1155 "src/Parser/TemplateParser.y" - public function yy_r164(){ +// line 1233 "src/Parser/TemplateParser.y" + public function yy_r171(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor, '', 'method'); } -// line 1160 "src/Parser/TemplateParser.y" - public function yy_r165(){ +// line 1238 "src/Parser/TemplateParser.y" + public function yy_r172(){ $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, 'method'); } -// line 1165 "src/Parser/TemplateParser.y" - public function yy_r166(){ +// line 1243 "src/Parser/TemplateParser.y" + public function yy_r173(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor, ''); } -// line 1170 "src/Parser/TemplateParser.y" - public function yy_r167(){ +// line 1248 "src/Parser/TemplateParser.y" + public function yy_r174(){ $this->_retvalue = array($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor, 'property'); } -// line 1175 "src/Parser/TemplateParser.y" - public function yy_r168(){ +// line 1253 "src/Parser/TemplateParser.y" + public function yy_r175(){ $this->_retvalue = array($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor.$this->yystack[$this->yyidx + 0]->minor, 'property'); } -// line 1181 "src/Parser/TemplateParser.y" - public function yy_r169(){ +// line 1259 "src/Parser/TemplateParser.y" + public function yy_r176(){ $this->_retvalue = ' '. trim($this->yystack[$this->yyidx + 0]->minor) . ' '; } -// line 1185 "src/Parser/TemplateParser.y" - public function yy_r170(){ +// line 1263 "src/Parser/TemplateParser.y" + public function yy_r177(){ static $lops = array( 'eq' => ' == ', 'ne' => ' != ', @@ -2809,8 +2929,8 @@ public static $yy_action = array( $op = strtolower(preg_replace('/\s*/', '', $this->yystack[$this->yyidx + 0]->minor)); $this->_retvalue = $lops[$op]; } -// line 1204 "src/Parser/TemplateParser.y" - public function yy_r171(){ +// line 1282 "src/Parser/TemplateParser.y" + public function yy_r178(){ static $tlops = array( 'isdivby' => array('op' => ' % ', 'pre' => '!('), 'isnotdivby' => array('op' => ' % ', 'pre' => '('), @@ -2822,8 +2942,8 @@ public static $yy_action = array( $op = strtolower(preg_replace('/\s*/', '', $this->yystack[$this->yyidx + 0]->minor)); $this->_retvalue = $tlops[$op]; } -// line 1217 "src/Parser/TemplateParser.y" - public function yy_r172(){ +// line 1295 "src/Parser/TemplateParser.y" + public function yy_r179(){ static $scond = array ( 'iseven' => '!(1 & ', 'isnoteven' => '(1 & ', @@ -2833,54 +2953,54 @@ public static $yy_action = array( $op = strtolower(str_replace(' ', '', $this->yystack[$this->yyidx + 0]->minor)); $this->_retvalue = $scond[$op]; } -// line 1231 "src/Parser/TemplateParser.y" - public function yy_r173(){ +// line 1309 "src/Parser/TemplateParser.y" + public function yy_r180(){ $this->_retvalue = 'array('.$this->yystack[$this->yyidx + -1]->minor.')'; } -// line 1242 "src/Parser/TemplateParser.y" - public function yy_r176(){ +// line 1320 "src/Parser/TemplateParser.y" + public function yy_r183(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.','.$this->yystack[$this->yyidx + 0]->minor; } -// line 1250 "src/Parser/TemplateParser.y" - public function yy_r178(){ +// line 1328 "src/Parser/TemplateParser.y" + public function yy_r185(){ $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor.'=>'.$this->yystack[$this->yyidx + 0]->minor; } -// line 1254 "src/Parser/TemplateParser.y" - public function yy_r179(){ +// line 1332 "src/Parser/TemplateParser.y" + public function yy_r186(){ $this->_retvalue = '\''.$this->yystack[$this->yyidx + -2]->minor.'\'=>'.$this->yystack[$this->yyidx + 0]->minor; } -// line 1270 "src/Parser/TemplateParser.y" - public function yy_r182(){ +// line 1348 "src/Parser/TemplateParser.y" + public function yy_r189(){ $this->compiler->leaveDoubleQuote(); $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor->to_smarty_php($this); } -// line 1276 "src/Parser/TemplateParser.y" - public function yy_r183(){ +// line 1354 "src/Parser/TemplateParser.y" + public function yy_r190(){ $this->yystack[$this->yyidx + -1]->minor->append_subtree($this, $this->yystack[$this->yyidx + 0]->minor); $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } -// line 1281 "src/Parser/TemplateParser.y" - public function yy_r184(){ +// line 1359 "src/Parser/TemplateParser.y" + public function yy_r191(){ $this->_retvalue = new Dq($this, $this->yystack[$this->yyidx + 0]->minor); } -// line 1285 "src/Parser/TemplateParser.y" - public function yy_r185(){ +// line 1363 "src/Parser/TemplateParser.y" + public function yy_r192(){ $this->_retvalue = new Code('(string)'.$this->yystack[$this->yyidx + -1]->minor); } -// line 1289 "src/Parser/TemplateParser.y" - public function yy_r186(){ +// line 1367 "src/Parser/TemplateParser.y" + public function yy_r193(){ $this->_retvalue = new Code('(string)('.$this->yystack[$this->yyidx + -1]->minor.')'); } -// line 1293 "src/Parser/TemplateParser.y" - public function yy_r187(){ +// line 1371 "src/Parser/TemplateParser.y" + public function yy_r194(){ $this->_retvalue = new Code('(string)$_smarty_tpl->getValue(\''. substr($this->yystack[$this->yyidx + 0]->minor,1) .'\')'); } -// line 1305 "src/Parser/TemplateParser.y" - public function yy_r190(){ +// line 1383 "src/Parser/TemplateParser.y" + public function yy_r197(){ $this->_retvalue = new Tag($this, $this->yystack[$this->yyidx + 0]->minor); } -// line 1309 "src/Parser/TemplateParser.y" - public function yy_r191(){ +// line 1387 "src/Parser/TemplateParser.y" + public function yy_r198(){ $this->_retvalue = new DqContent($this->yystack[$this->yyidx + 0]->minor); } diff --git a/src/Parser/TemplateParser.y b/src/Parser/TemplateParser.y index f1e3c35..58d115f 100644 --- a/src/Parser/TemplateParser.y +++ b/src/Parser/TemplateParser.y @@ -19,7 +19,7 @@ use \Smarty\ParseTree\Code; use \Smarty\ParseTree\Dq; use \Smarty\ParseTree\DqContent; use \Smarty\ParseTree\Tag; - +use \Smarty\CompilerException; /** * Smarty Template Parser Class @@ -306,7 +306,8 @@ smartytag(A) ::= SIMPELOUTPUT(B). { $attributes[] = 'nocache'; $var = $match[1]; } - A = $this->compiler->compilePrintExpression($this->compiler->compileVariable('\''.$var.'\''), $attributes); + $this->compiler->triggerTagNoCache($var); + A = $this->compiler->compilePrintExpression('$_smarty_tpl->getValue(\''.$var.'\')', $attributes); } // simple tag like {name} @@ -375,7 +376,7 @@ outattr(A) ::= output(B) attributes(C). { A = array(B,C); } -output(A) ::= variable(B). { +output(A) ::= variablevalue(B). { A = B; } output(A) ::= value(B). { @@ -689,7 +690,8 @@ nullcoalescing(res) ::= expr(v) QMARK QMARK expr(e2). { // ternary // ternary(res) ::= expr(v) QMARK DOLLARID(e1) COLON expr(e2). { - res = v.' ? '. $this->compiler->compileVariable('\''.substr(e1,1).'\'') . ' : '.e2; + $this->compiler->triggerTagNoCache(substr(e1,1)); + res = v.' ? $_smarty_tpl->getValue(\''.substr(e1,1).'\') : '.e2; } ternary(res) ::= expr(v) QMARK value(e1) COLON expr(e2). { @@ -706,7 +708,7 @@ ternary(res) ::= expr(v) QMARK COLON expr(e2). { } // value -value(res) ::= variable(v). { +value(res) ::= variablevalue(v). { res = v; } @@ -724,7 +726,7 @@ value(res) ::= TYPECAST(t) value(v). { res = t.v; } -value(res) ::= variable(v) INCDEC(o). { +value(res) ::= variablevalue(v) INCDEC(o). { res = v.o; } @@ -771,10 +773,10 @@ value(res) ::= OPENP expr(e) CLOSEP. { res = '('. e .')'; } -value(res) ::= variable(v1) INSTANCEOF(i) ns1(v2). { +value(res) ::= variablevalue(v1) INSTANCEOF(i) ns1(v2). { res = v1.i.v2; } -value(res) ::= variable(v1) INSTANCEOF(i) variable(v2). { +value(res) ::= variablevalue(v1) INSTANCEOF(i) variablevalue(v2). { res = v1.i.v2; } @@ -797,7 +799,8 @@ value(res) ::= varindexed(vi) DOUBLECOLON static_class_access(r). { if (vi['var'] === '\'smarty\'') { $this->compiler->appendPrefixCode("compile(array(),$this->compiler,vi['smarty_internal_index']).';?>'); } else { - $this->compiler->appendPrefixCode("compiler->compileVariable(vi['var']).vi['smarty_internal_index'].';?>'); + $this->compiler->triggerTagNoCache(vi['var']); + $this->compiler->appendPrefixCode("getValue(" . vi['var'] . ')'.vi['smarty_internal_index'].';?>'); } res = $prefixVar .'::'.r[0].r[1]; } @@ -847,54 +850,88 @@ ns1(res) ::= NAMESPACE(i). { } +// variable lists +// multiple variables +variablelist(res) ::= variablelist(l) COMMA variable(v). { + res = array_merge(l,array(v)); +} + +variablelist(res) ::= variablelist(l) COMMA expr(e). { + res = array_merge(l,array(e)); +} + +// single variable +variablelist(res) ::= variable(v). { + res = array(v); +} + +// single expression +variablelist(res) ::= expr(e). { + res = array(e); +} + +// no variable +variablelist(res) ::= . { + res = array(); +} // // variables // // Smarty variable (optional array) variable(res) ::= DOLLARID(i). { - res = $this->compiler->compileVariable('\''.substr(i,1).'\''); + $this->compiler->triggerTagNoCache(substr(i,1)); + res = array('$_smarty_tpl->hasVariable(\''.substr(i,1).'\')','$_smarty_tpl->getValue(\''.substr(i,1).'\')'); } variable(res) ::= varindexed(vi). { if (vi['var'] === '\'smarty\'') { $smarty_var = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']); - res = $smarty_var; + res = array('true', $smarty_var); } else { // used for array reset,next,prev,end,current $this->last_variable = vi['var']; $this->last_index = vi['smarty_internal_index']; - res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index']; + $this->compiler->triggerTagNoCache(vi['var']); + res = array('true', '$_smarty_tpl->getValue(' . vi['var'] . ')'.vi['smarty_internal_index']); } } // variable with property variable(res) ::= varvar(v) AT ID(p). { - res = '$_smarty_tpl->getVariable('. v .')->'.p; + res = array('true', '$_smarty_tpl->getVariable('. v .')->'.p); } // object variable(res) ::= object(o). { - res = o; + res = array('true', o); } // config variable -variable(res) ::= HATCH ID(i) HATCH. { +configvariable(res) ::= HATCH ID(i) HATCH. { res = $this->compiler->compileConfigVariable('\'' . i . '\''); } -variable(res) ::= HATCH ID(i) HATCH arrayindex(a). { +configvariable(res) ::= HATCH ID(i) HATCH arrayindex(a). { res = '(is_array($tmp = ' . $this->compiler->compileConfigVariable('\'' . i . '\'') . ') ? $tmp'.a.' :null)'; } -variable(res) ::= HATCH variable(v) HATCH. { +configvariable(res) ::= HATCH variablevalue(v) HATCH. { res = $this->compiler->compileConfigVariable(v); } -variable(res) ::= HATCH variable(v) HATCH arrayindex(a). { +configvariable(res) ::= HATCH variablevalue(v) HATCH arrayindex(a). { res = '(is_array($tmp = ' . $this->compiler->compileConfigVariable(v) . ') ? $tmp'.a.' : null)'; } +variablevalue(res) ::= variable(v). { + res = v[1]; +} + +variablevalue(res) ::= configvariable(v). { + res = v; +} + varindexed(res) ::= DOLLARID(i) arrayindex(a). { res = array('var'=>'\''.substr(i,1).'\'', 'smarty_internal_index'=>a); } @@ -918,14 +955,17 @@ arrayindex ::= . { // single index definition // Smarty2 style index indexdef(res) ::= DOT DOLLARID(i). { - res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']'; + $this->compiler->triggerTagNoCache(substr(i,1)); + res = '[$_smarty_tpl->getValue(\''.substr(i,1).'\')]'; } indexdef(res) ::= DOT varvar(v). { - res = '['.$this->compiler->compileVariable(v).']'; + $this->compiler->triggerTagNoCache(v); + res = '[$_smarty_tpl->getValue(' . v . ')]'; } indexdef(res) ::= DOT varvar(v) AT ID(p). { - res = '['.$this->compiler->compileVariable(v).'->'.p.']'; + $this->compiler->triggerTagNoCache(v); + res = '[$_smarty_tpl->getValue(' . v . ')->'.p.']'; } indexdef(res) ::= DOT ID(i). { @@ -956,9 +996,10 @@ indexdef(res) ::= OPENB INTEGER(n) CLOSEB. { res = '['.n.']'; } indexdef(res) ::= OPENB DOLLARID(i) CLOSEB. { - res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']'; + $this->compiler->triggerTagNoCache(substr(i,1)); + res = '[$_smarty_tpl->getValue(\''.substr(i,1).'\')]'; } -indexdef(res) ::= OPENB variable(v) CLOSEB. { +indexdef(res) ::= OPENB variablevalue(v) CLOSEB. { res = '['.v.']'; } indexdef(res) ::= OPENB value(v) CLOSEB. { @@ -1000,7 +1041,8 @@ varvarele(res) ::= ID(s). { } varvarele(res) ::= SIMPELOUTPUT(i). { $var = trim(substr(i, $this->compiler->getLdelLength(), -$this->compiler->getRdelLength()), ' $'); - res = $this->compiler->compileVariable('\''.$var.'\''); + $this->compiler->triggerTagNoCache($var); + res = '$_smarty_tpl->getValue(\''.$var.'\')'; } // variable sections of element @@ -1015,7 +1057,8 @@ object(res) ::= varindexed(vi) objectchain(oc). { if (vi['var'] === '\'smarty\'') { res = (new \Smarty\Compile\SpecialVariableCompiler())->compile(array(),$this->compiler,vi['smarty_internal_index']).oc; } else { - res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index'].oc; + $this->compiler->triggerTagNoCache(vi['var']); + res = '$_smarty_tpl->getValue(' . vi['var'] . ')'.vi['smarty_internal_index'].oc; } } @@ -1041,7 +1084,8 @@ objectelement(res)::= PTR varvar(v) arrayindex(a). { if ($this->security) { $this->compiler->trigger_template_error (self::ERR2); } - res = '->{'.$this->compiler->compileVariable(v).a.'}'; + $this->compiler->triggerTagNoCache(v); + res = '->{$_smarty_tpl->getValue(' . v . ')'.a.'}'; } objectelement(res)::= PTR LDEL expr(e) RDEL arrayindex(a). { @@ -1067,8 +1111,41 @@ objectelement(res)::= PTR method(f). { // // function // -function(res) ::= ns1(f) OPENP params(p) CLOSEP. { - res = $this->compiler->compileModifierInExpression(f, p); +function(res) ::= ns1(f) OPENP variablelist(v) CLOSEP. { + + if (f == 'isset') { + res = '(true'; + if (count(v) == 0) { + throw new CompilerException("Invalid number of arguments for isset. isset expects at least one parameter."); + } + foreach (v as $value) { + if (is_array($value)) { + res .= ' && (' . $value[0] . ' && null !== (' . $value[1] . ' ?? null))'; + } else { + res .= ' && (' . $value . ' !== null)'; + } + } + res .= ')'; + } elseif (f == 'empty') { + if (count(v) != 1) { + throw new CompilerException("Invalid number of arguments for empty. empty expects at exactly one parameter."); + } + if (is_array(v[0])) { + res .= '( !' . v[0][0] . ' || empty(' . v[0][1] . '))'; + } else { + res = 'false == ' . v[0]; + } + } else { + $p = array(); + foreach (v as $value) { + if (is_array($value)) { + $p[] = $value[1]; + } else { + $p[] = $value; + } + } + res = $this->compiler->compileModifierInExpression(f, $p); + } } @@ -1087,7 +1164,8 @@ method(res) ::= DOLLARID(f) OPENP params(p) CLOSEP. { $this->compiler->trigger_template_error (self::ERR2); } $prefixVar = $this->compiler->getNewPrefixVariable(); - $this->compiler->appendPrefixCode("compiler->compileVariable('\''.substr(f,1).'\'').';?>'); + $this->compiler->triggerTagNoCache(substr(f,1)); + $this->compiler->appendPrefixCode("getValue('".substr(f,1).'\')'.';?>'); res = $prefixVar .'('. implode(',',p) .')'; } @@ -1282,7 +1360,7 @@ doublequoted(res) ::= doublequotedcontent(o). { res = new Dq($this, o); } -doublequotedcontent(res) ::= BACKTICK variable(v) BACKTICK. { +doublequotedcontent(res) ::= BACKTICK variablevalue(v) BACKTICK. { res = new Code('(string)'.v); } @@ -1294,7 +1372,7 @@ doublequotedcontent(res) ::= DOLLARID(i). { res = new Code('(string)$_smarty_tpl->getValue(\''. substr(i,1) .'\')'); } -doublequotedcontent(res) ::= LDEL variable(v) RDEL. { +doublequotedcontent(res) ::= LDEL variablevalue(v) RDEL. { res = new Code('(string)'.v); } diff --git a/src/Resource/BasePlugin.php b/src/Resource/BasePlugin.php index 56f7dfa..6d22222 100644 --- a/src/Resource/BasePlugin.php +++ b/src/Resource/BasePlugin.php @@ -112,7 +112,7 @@ abstract class BasePlugin * @param Source $source source object * @param Template|null $_template template object */ - abstract public function populate(Source $source, \Smarty\Template $_template = null); + abstract public function populate(Source $source, ?\Smarty\Template $_template = null); /** * populate Source Object with timestamp and exists from Resource diff --git a/src/Resource/CustomPlugin.php b/src/Resource/CustomPlugin.php index b50ef7a..895e971 100644 --- a/src/Resource/CustomPlugin.php +++ b/src/Resource/CustomPlugin.php @@ -50,7 +50,7 @@ abstract class CustomPlugin extends BasePlugin { * @param Source $source source object * @param Template|null $_template template object */ - public function populate(Source $source, Template $_template = null) { + public function populate(Source $source, ?Template $_template = null) { $source->uid = sha1($source->type . ':' . $source->name); $mtime = $this->fetchTimestamp($source->name); if ($mtime !== null) { diff --git a/src/Resource/ExtendsPlugin.php b/src/Resource/ExtendsPlugin.php index acce54e..960e379 100644 --- a/src/Resource/ExtendsPlugin.php +++ b/src/Resource/ExtendsPlugin.php @@ -23,7 +23,7 @@ class ExtendsPlugin extends BasePlugin * * @throws Exception */ - public function populate(Source $source, Template $_template = null) + public function populate(Source $source, ?Template $_template = null) { $uid = ''; $sources = array(); @@ -93,7 +93,11 @@ class ExtendsPlugin extends BasePlugin */ public function getBasename(Source $source) { - return str_replace(':', '.', basename($source->getResourceName())); + $search = array(':'); + if (\Smarty\Smarty::$_IS_WINDOWS) { + $search = array(':', '|'); + } + return str_replace($search, '.', basename($source->getResourceName())); } /* diff --git a/src/Resource/FilePlugin.php b/src/Resource/FilePlugin.php index c595957..0033c83 100644 --- a/src/Resource/FilePlugin.php +++ b/src/Resource/FilePlugin.php @@ -32,7 +32,7 @@ class FilePlugin extends BasePlugin { * * @throws Exception */ - public function populate(Source $source, Template $_template = null) { + public function populate(Source $source, ?Template $_template = null) { $source->uid = sha1( $source->name . ($source->isConfig ? $source->getSmarty()->_joined_config_dir : diff --git a/src/Resource/StreamPlugin.php b/src/Resource/StreamPlugin.php index 91afffd..9b5b3f5 100644 --- a/src/Resource/StreamPlugin.php +++ b/src/Resource/StreamPlugin.php @@ -33,7 +33,7 @@ class StreamPlugin extends RecompiledPlugin { * * @return void */ - public function populate(Source $source, Template $_template = null) { + public function populate(Source $source, ?Template $_template = null) { $source->uid = false; $source->content = $this->getContent($source); $source->timestamp = $source->exists = !!$source->content; diff --git a/src/Resource/StringPlugin.php b/src/Resource/StringPlugin.php index 6b5bee4..fb3692c 100644 --- a/src/Resource/StringPlugin.php +++ b/src/Resource/StringPlugin.php @@ -31,7 +31,7 @@ class StringPlugin extends BasePlugin { * * @return void */ - public function populate(Source $source, Template $_template = null) { + public function populate(Source $source, ?Template $_template = null) { $source->uid = sha1($source->name); $source->timestamp = $source->exists = true; } diff --git a/src/Runtime/DefaultPluginHandlerRuntime.php b/src/Runtime/DefaultPluginHandlerRuntime.php index ad6ed74..f3be85c 100644 --- a/src/Runtime/DefaultPluginHandlerRuntime.php +++ b/src/Runtime/DefaultPluginHandlerRuntime.php @@ -26,7 +26,7 @@ class DefaultPluginHandlerRuntime { $script = null; $cacheable = null; - return (call_user_func_array( + return (\call_user_func_array( $this->defaultPluginHandler, [ $tag, @@ -54,7 +54,7 @@ class DefaultPluginHandlerRuntime { $script = null; $cacheable = null; - if (call_user_func_array( + if (\call_user_func_array( $this->defaultPluginHandler, [ $tag, diff --git a/src/Runtime/InheritanceRuntime.php b/src/Runtime/InheritanceRuntime.php index ffd7aae..74ea854 100644 --- a/src/Runtime/InheritanceRuntime.php +++ b/src/Runtime/InheritanceRuntime.php @@ -162,7 +162,7 @@ class InheritanceRuntime { private function processBlock( Template $tpl, \Smarty\Runtime\Block $block, - \Smarty\Runtime\Block $parent = null + ?\Smarty\Runtime\Block $parent = null ) { if ($block->hide && !isset($block->child)) { return; diff --git a/src/Smarty.php b/src/Smarty.php index a8b6685..15baba8 100644 --- a/src/Smarty.php +++ b/src/Smarty.php @@ -54,7 +54,7 @@ class Smarty extends \Smarty\TemplateBase { /** * smarty version */ - const SMARTY_VERSION = '5.4.0'; + const SMARTY_VERSION = '5.4.3'; /** * define caching modes diff --git a/src/Template.php b/src/Template.php index fcb0f58..242fb23 100644 --- a/src/Template.php +++ b/src/Template.php @@ -115,7 +115,7 @@ class Template extends TemplateBase { public function __construct( $template_resource, Smarty $smarty, - \Smarty\Data $_parent = null, + ?\Smarty\Data $_parent = null, $_cache_id = null, $_compile_id = null, $_caching = null, @@ -248,7 +248,7 @@ class Template extends TemplateBase { $caching, $cache_lifetime, array $extra_vars = [], - int $scope = null, + ?int $scope = null, ?string $currentDir = null ) { @@ -462,7 +462,7 @@ class Template extends TemplateBase { * @return string * @throws Exception */ - public function createCodeFrame($content = '', $functions = '', $cache = false, \Smarty\Compiler\Template $compiler = null) { + public function createCodeFrame($content = '', $functions = '', $cache = false, ?\Smarty\Compiler\Template $compiler = null) { return $this->getCodeFrameCompiler()->create($content, $functions, $cache, $compiler); } diff --git a/src/Template/Source.php b/src/Template/Source.php index 382e710..2c2022e 100644 --- a/src/Template/Source.php +++ b/src/Template/Source.php @@ -134,9 +134,9 @@ class Source { * @throws Exception */ public static function load( - Template $_template = null, - Smarty $smarty = null, - $template_resource = null + ?Template $_template = null, + ?Smarty $smarty = null, + $template_resource = null ) { if ($_template) { $smarty = $_template->getSmarty(); @@ -203,7 +203,7 @@ class Source { */ public function _getDefaultTemplate($default_handler) { $_content = $_timestamp = null; - $_return = call_user_func_array( + $_return = \call_user_func_array( $default_handler, [$this->type, $this->name, &$_content, &$_timestamp, $this->smarty] ); diff --git a/src/TemplateBase.php b/src/TemplateBase.php index 3674edf..f01d110 100644 --- a/src/TemplateBase.php +++ b/src/TemplateBase.php @@ -179,7 +179,7 @@ abstract class TemplateBase extends Data { * @api Smarty::createData() * */ - public function createData(Data $parent = null, $name = null) { + public function createData(?Data $parent = null, $name = null) { /* @var Smarty $smarty */ $smarty = $this->getSmarty(); $dataObj = new Data($parent, $smarty, $name); diff --git a/update/ReadMe.md b/update/ReadMe.md new file mode 100644 index 0000000..8a687d5 --- /dev/null +++ b/update/ReadMe.md @@ -0,0 +1,15 @@ +# Files in this folder must be copied or merged over + +The following files can be copied as is + +- `src/BlockHandler/T.php` +- `src/FunctionHandler/Popup.php` +- `src/FunctionHandler/PopupInit.php` + +The following files need to be checkd for changes. + +If no changes they can be copied, else merged + +- `src/FunctionHandler/HtmlCheckboxes.php` +- `src/FunctionHandler/HtmlOptions.php` +- `src/Extensions/DefaultExtensions.php` diff --git a/update/src/BlockHandler/T.php b/update/src/BlockHandler/T.php new file mode 100644 index 0000000..d04ea6a --- /dev/null +++ b/update/src/BlockHandler/T.php @@ -0,0 +1,238 @@ + + * @throws \Smarty\Exception + */ +class T implements BlockHandlerInterface +{ + /** + * Replaces arguments in a string with their values. + * Arguments are represented by % followed by their number. + * + * @param string $str Source string + * @param mixed mixed Arguments, can be passed in an array or through single variables. + * @return string Modified string + */ + private function smartyGettextStrArg($str/*, $varargs... */) + { + $tr = []; + $p = 0; + + $nargs = func_num_args(); + for ($i = 1; $i < $nargs; $i++) { + $arg = func_get_arg($i); + + if (is_array($arg)) { + foreach ($arg as $aarg) { + $tr['%' . ++$p] = $aarg; + } + } else { + $tr['%' . ++$p] = $arg; + } + } + + return strtr($str, $tr); + } + + /** + * raise a notice for missing callable, indicates L10n functions where not loaded + * + * @param string $function + * @return void + */ + private function reportL10nNotInitialized(string $function): void + { + trigger_error("Missing " . $function . ", L10n::loadFunctions() called?", E_NOTICE); + } + + /** + * main handler + * + * @param array $params + * @param string $content + * @param Template $template + * @param boolean $repeat + * @return void + */ + public function handle($params, $content, Template $template, &$repeat) + { + if (is_null($content)) { + return; + } + + $escape = 'html'; + $plural = null; + $count = null; + $domain = null; + $context = null; + + foreach ($params as $_key => $_value) { + switch ($_key) { + // set escape mode, default html escape + case 'escape': + $escape = (string)$_value; + break; + // set plural parameters 'plural' and 'count'. + case 'plural': + $plural = $_value; + break; + // set count, only for plural, else ignored + case 'count': + $count = $_value; + break; + // get domain param + case 'domain': + $domain = (string)$_value; + break; + // get context param + case 'context': + $context = (string)$_value; + break; + } + } + + // use plural if required parameters are set + if (isset($count) && isset($plural)) { + // plural calls + if (isset($domain) && isset($context)) { + if (is_callable('_dnpgettext')) { + $content = _dnpgettext($domain, $context, $content, $plural, $count); + } else { + $this->reportL10nNotInitialized('_dnpgettext'); + } + } elseif (isset($domain)) { + if (is_callable('_dngettext')) { + $content = _dngettext($domain, $content, $plural, $count); + } else { + $this->reportL10nNotInitialized('_dngettext'); + } + } elseif (isset($context)) { + if (is_callable('_npgettext')) { + $content = _npgettext($context, $content, $plural, $count); + } else { + $this->reportL10nNotInitialized('_npgettext'); + } + } else { + if (is_callable('_ngettext')) { + $content = _ngettext($content, $plural, $count); + } else { + $this->reportL10nNotInitialized('_ngettext'); + } + } + } else { + // use normal + if (isset($domain) && isset($context)) { + if (is_callable('_dpgettext')) { + $content = _dpgettext($domain, $context, $content); + } else { + $this->reportL10nNotInitialized('_dpgettext'); + } + } elseif (isset($domain)) { + if (is_callable('_dgettext')) { + $content = _dgettext($domain, $content); + } else { + $this->reportL10nNotInitialized('_dgettext'); + } + } elseif (isset($context)) { + if (is_callable('_pgettext')) { + $content = _pgettext($context, $content); + } else { + $this->reportL10nNotInitialized('_pgettext'); + } + } else { + if (is_callable('_gettext')) { + $content = _gettext($content); + } else { + $this->reportL10nNotInitialized('_gettext'); + } + } + } + + // run strarg if there are parameters + if (count($params)) { + $content = $this->smartyGettextStrArg($content, $params); + } + + switch ($escape) { + case 'html': + // default + $content = nl2br(htmlspecialchars($content)); + break; + case 'javascript': + case 'js': + // javascript escape + $content = strtr( + $content, + [ + '\\' => '\\\\', + "'" => "\\'", + '"' => '\\"', + "\r" => '\\r', + "\n" => '\\n', + ' '<\/' + ] + ); + break; + case 'url': + // url escape + $content = urlencode($content); + break; + // below is a list for explicit OFF + case 'no': + case 'off': + case 'false': + case '0': + case 0: + // explicit OFF + default: + break; + } + + return $content; + } + + public function isCacheable(): bool + { + return true; + } +} + +// __END__ diff --git a/update/src/Extensions/DefaultExtension.php b/update/src/Extensions/DefaultExtension.php new file mode 100644 index 0000000..156e4ec --- /dev/null +++ b/update/src/Extensions/DefaultExtension.php @@ -0,0 +1,760 @@ +modifiers[$modifier])) { + return $this->modifiers[$modifier]; + } + + switch ($modifier) { + case 'cat': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CatModifierCompiler(); break; + case 'count_characters': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountCharactersModifierCompiler(); break; + case 'count_paragraphs': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountParagraphsModifierCompiler(); break; + case 'count_sentences': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountSentencesModifierCompiler(); break; + case 'count_words': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountWordsModifierCompiler(); break; + case 'default': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\DefaultModifierCompiler(); break; + case 'empty': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EmptyModifierCompiler(); break; + case 'escape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EscapeModifierCompiler(); break; + case 'from_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\FromCharsetModifierCompiler(); break; + case 'indent': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IndentModifierCompiler(); break; + case 'is_array': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IsArrayModifierCompiler(); break; + case 'isset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IssetModifierCompiler(); break; + case 'json_encode': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\JsonEncodeModifierCompiler(); break; + case 'lower': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\LowerModifierCompiler(); break; + case 'nl2br': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\Nl2brModifierCompiler(); break; + case 'noprint': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\NoPrintModifierCompiler(); break; + case 'raw': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\RawModifierCompiler(); break; + case 'round': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\RoundModifierCompiler(); break; + case 'str_repeat': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StrRepeatModifierCompiler(); break; + case 'string_format': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StringFormatModifierCompiler(); break; + case 'strip': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StripModifierCompiler(); break; + case 'strip_tags': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StripTagsModifierCompiler(); break; + case 'strlen': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StrlenModifierCompiler(); break; + case 'substr': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\SubstrModifierCompiler(); break; + case 'to_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\ToCharsetModifierCompiler(); break; + case 'unescape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\UnescapeModifierCompiler(); break; + case 'upper': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\UpperModifierCompiler(); break; + case 'wordwrap': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\WordWrapModifierCompiler(); break; + } + + return $this->modifiers[$modifier] ?? null; + } + + public function getModifierCallback(string $modifierName) { + switch ($modifierName) { + case 'capitalize': return [$this, 'smarty_modifier_capitalize']; + case 'count': return [$this, 'smarty_modifier_count']; + case 'date_format': return [$this, 'smarty_modifier_date_format']; + case 'debug_print_var': return [$this, 'smarty_modifier_debug_print_var']; + case 'escape': return [$this, 'smarty_modifier_escape']; + case 'explode': return [$this, 'smarty_modifier_explode']; + case 'implode': return [$this, 'smarty_modifier_implode']; + case 'in_array': return [$this, 'smarty_modifier_in_array']; + case 'join': return [$this, 'smarty_modifier_join']; + case 'mb_wordwrap': return [$this, 'smarty_modifier_mb_wordwrap']; + case 'number_format': return [$this, 'smarty_modifier_number_format']; + case 'regex_replace': return [$this, 'smarty_modifier_regex_replace']; + case 'replace': return [$this, 'smarty_modifier_replace']; + case 'spacify': return [$this, 'smarty_modifier_spacify']; + case 'split': return [$this, 'smarty_modifier_split']; + case 'truncate': return [$this, 'smarty_modifier_truncate']; + } + return null; + } + + public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface { + + if (isset($this->functionHandlers[$functionName])) { + return $this->functionHandlers[$functionName]; + } + + switch ($functionName) { + case 'count': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Count(); break; + case 'counter': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Counter(); break; + case 'cycle': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Cycle(); break; + case 'fetch': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Fetch(); break; + case 'html_checkboxes': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlCheckboxes(); break; + case 'html_image': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlImage(); break; + case 'html_options': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlOptions(); break; + case 'html_radios': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlRadios(); break; + case 'html_select_date': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlSelectDate(); break; + case 'html_select_time': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlSelectTime(); break; + case 'html_table': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlTable(); break; + case 'mailto': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Mailto(); break; + case 'math': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Math(); break; + case 'popup_init': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\PopupInit(); break; + case 'popup': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Popup(); break; + } + + return $this->functionHandlers[$functionName] ?? null; + } + + public function getBlockHandler(string $blockTagName): ?\Smarty\BlockHandler\BlockHandlerInterface { + + switch ($blockTagName) { + case 'textformat': $this->blockHandlers[$blockTagName] = new \Smarty\BlockHandler\TextFormat(); break; + case 't': $this->blockHandlers[$blockTagName] = new \Smarty\BlockHandler\T(); break; + } + + return $this->blockHandlers[$blockTagName] ?? null; + } + + /** + * Smarty spacify modifier plugin + * Type: modifier + * Name: spacify + * Purpose: add spaces between characters in a string + * + * @author Monte Ohrt + * + * @param string $string input string + * @param string $spacify_char string to insert between characters. + * + * @return string + */ + public function smarty_modifier_spacify($string, $spacify_char = ' ') + { + // well… what about charsets besides latin and UTF-8? + return implode($spacify_char, preg_split('//' . \Smarty\Smarty::$_UTF8_MODIFIER, $string, -1, PREG_SPLIT_NO_EMPTY)); + } + + /** + * Smarty capitalize modifier plugin + * Type: modifier + * Name: capitalize + * Purpose: capitalize words in the string + * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }} + * + * @param string $string string to capitalize + * @param boolean $uc_digits also capitalize "x123" to "X123" + * @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa" + * + * @return string capitalized string + * @author Monte Ohrt + * @author Rodney Rehm + */ + public function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) + { + $string = (string) $string; + + if ($lc_rest) { + // uppercase (including hyphenated words) + $upper_string = mb_convert_case($string, MB_CASE_TITLE, \Smarty\Smarty::$_CHARSET); + } else { + // uppercase word breaks + $upper_string = preg_replace_callback( + "!(^|[^\p{L}'])([\p{Ll}])!S" . \Smarty\Smarty::$_UTF8_MODIFIER, + function ($matches) { + return stripslashes($matches[1]) . + mb_convert_case(stripslashes($matches[2]), MB_CASE_UPPER, \Smarty\Smarty::$_CHARSET); + }, + $string + ); + } + // check uc_digits case + if (!$uc_digits) { + if (preg_match_all( + "!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . \Smarty\Smarty::$_UTF8_MODIFIER, + $string, + $matches, + PREG_OFFSET_CAPTURE + ) + ) { + foreach ($matches[ 1 ] as $match) { + $upper_string = + substr_replace( + $upper_string, + mb_strtolower($match[ 0 ], \Smarty\Smarty::$_CHARSET), + $match[ 1 ], + strlen($match[ 0 ]) + ); + } + } + } + $upper_string = + preg_replace_callback( + "!((^|\s)['\"])(\w)!" . \Smarty\Smarty::$_UTF8_MODIFIER, + function ($matches) { + return stripslashes( + $matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 3 ]), + MB_CASE_UPPER, + \Smarty\Smarty::$_CHARSET + ); + }, + $upper_string + ); + return $upper_string; + } + + /** + * Smarty count modifier plugin + * Type: modifier + * Name: count + * Purpose: counts all elements in an array or in a Countable object + * Input: + * - Countable|array: array or object to count + * - mode: int defaults to 0 for normal count mode, if set to 1 counts recursive + * + * @param mixed $arrayOrObject input array/object + * @param int $mode count mode + * + * @return int + */ + public function smarty_modifier_count($arrayOrObject, $mode = 0) { + /* + * @see https://www.php.net/count + * > Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface, + * > 1 would be returned, unless value was null, in which case 0 would be returned. + */ + + if ($arrayOrObject instanceof \Countable || is_array($arrayOrObject)) { + return count($arrayOrObject, (int) $mode); + } elseif ($arrayOrObject === null) { + return 0; + } + return 1; + } + + /** + * Smarty date_format modifier plugin + * Type: modifier + * Name: date_format + * Purpose: format datestamps via strftime + * Input: + * - string: input date string + * - format: strftime format for output + * - default_date: default date if $string is empty + * + * @author Monte Ohrt + * + * @param string $string input date string + * @param string $format strftime format for output + * @param string $default_date default date if $string is empty + * @param string $formatter either 'strftime' or 'auto' + * + * @return string |void + * @uses smarty_make_timestamp() + */ + public function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') + { + if ($format === null) { + $format = \Smarty\Smarty::$_DATE_FORMAT; + } + + if (!empty($string) && $string !== '0000-00-00' && $string !== '0000-00-00 00:00:00') { + $timestamp = smarty_make_timestamp($string); + } elseif (!empty($default_date)) { + $timestamp = smarty_make_timestamp($default_date); + } else { + return; + } + if ($formatter === 'strftime' || ($formatter === 'auto' && strpos($format, '%') !== false)) { + if (\Smarty\Smarty::$_IS_WINDOWS) { + $_win_from = array( + '%D', + '%h', + '%n', + '%r', + '%R', + '%t', + '%T' + ); + $_win_to = array( + '%m/%d/%y', + '%b', + "\n", + '%I:%M:%S %p', + '%H:%M', + "\t", + '%H:%M:%S' + ); + if (strpos($format, '%e') !== false) { + $_win_from[] = '%e'; + $_win_to[] = sprintf('%\' 2d', date('j', $timestamp)); + } + if (strpos($format, '%l') !== false) { + $_win_from[] = '%l'; + $_win_to[] = sprintf('%\' 2d', date('h', $timestamp)); + } + $format = str_replace($_win_from, $_win_to, $format); + } + // @ to suppress deprecation errors when running in PHP8.1 or higher. + return @strftime($format, $timestamp); + } else { + return date($format, $timestamp); + } + } + + /** + * Smarty debug_print_var modifier plugin + * Type: modifier + * Name: debug_print_var + * Purpose: formats variable contents for display in the console + * + * @author Monte Ohrt + * + * @param array|object $var variable to be formatted + * @param int $max maximum recursion depth if $var is an array or object + * @param int $length maximum string length if $var is a string + * @param int $depth actual recursion depth + * @param array $objects processed objects in actual depth to prevent recursive object processing + * + * @return string + */ + public function smarty_modifier_debug_print_var($var, $max = 10, $length = 40, $depth = 0, $objects = array()) + { + $_replace = array("\n" => '\n', "\r" => '\r', "\t" => '\t'); + switch (gettype($var)) { + case 'array': + $results = 'Array (' . count($var) . ')'; + if ($depth === $max) { + break; + } + foreach ($var as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) . '' . strtr($curr_key, $_replace) . + ' => ' . + $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); + $depth--; + } + break; + case 'object': + $object_vars = get_object_vars($var); + $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; + if (in_array($var, $objects)) { + $results .= ' called recursive'; + break; + } + if ($depth === $max) { + break; + } + $objects[] = $var; + foreach ($object_vars as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) . ' ->' . strtr($curr_key, $_replace) . + ' = ' . $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); + $depth--; + } + break; + case 'boolean': + case 'NULL': + case 'resource': + if (true === $var) { + $results = 'true'; + } elseif (false === $var) { + $results = 'false'; + } elseif (null === $var) { + $results = 'null'; + } else { + $results = htmlspecialchars((string)$var); + } + $results = '' . $results . ''; + break; + case 'integer': + case 'float': + $results = htmlspecialchars((string)$var); + break; + case 'string': + $results = strtr($var, $_replace); + if (mb_strlen($var, \Smarty\Smarty::$_CHARSET) > $length) { + $results = mb_substr($var, 0, $length - 3, \Smarty\Smarty::$_CHARSET) . '...'; + } + $results = htmlspecialchars('"' . $results . '"', ENT_QUOTES, \Smarty\Smarty::$_CHARSET); + break; + case 'unknown type': + default: + $results = strtr((string)$var, $_replace); + if (mb_strlen($results, \Smarty\Smarty::$_CHARSET) > $length) { + $results = mb_substr($results, 0, $length - 3, \Smarty\Smarty::$_CHARSET) . '...'; + } + $results = htmlspecialchars($results, ENT_QUOTES, \Smarty\Smarty::$_CHARSET); + } + return $results; + } + + /** + * Smarty escape modifier plugin + * Type: modifier + * Name: escape + * Purpose: escape string for output + * + * @author Monte Ohrt + * + * @param string $string input string + * @param string $esc_type escape type + * @param string $char_set character set, used for htmlspecialchars() or htmlentities() + * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() + * + * @return string escaped input string + */ + public function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) + { + if (!$char_set) { + $char_set = \Smarty\Smarty::$_CHARSET; + } + + $string = (string)$string; + + switch ($esc_type) { + case 'html': + return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); + // no break + case 'htmlall': + $string = mb_convert_encoding($string, 'UTF-8', $char_set); + return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode); + // no break + case 'url': + return rawurlencode($string); + case 'urlpathinfo': + return str_replace('%2F', '/', rawurlencode($string)); + case 'quotes': + // escape unescaped single quotes + return preg_replace("%(?mb_to_unicode($string, \Smarty\Smarty::$_CHARSET) as $unicode) { + $return .= '&#x' . strtoupper(dechex($unicode)) . ';'; + } + return $return; + case 'decentity': + $return = ''; + foreach ($this->mb_to_unicode($string, \Smarty\Smarty::$_CHARSET) as $unicode) { + $return .= '&#' . $unicode . ';'; + } + return $return; + case 'javascript': + // escape quotes and backslashes, newlines, etc. + return strtr( + $string, + array( + '\\' => '\\\\', + "'" => "\\'", + '"' => '\\"', + "\r" => '\\r', + "\n" => '\\n', + ' '<\/', + // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements + '