├── test ├── templates │ ├── dirname.tpl │ ├── displayfoo.tpl │ ├── helloworld.tpl │ ├── helloworld2.tpl │ ├── relativity │ │ ├── foo.tpl │ │ ├── theory │ │ │ ├── foo.tpl │ │ │ ├── theory.tpl │ │ │ └── einstein │ │ │ │ ├── foo.tpl │ │ │ │ └── einstein.tpl │ │ └── relativity.tpl │ ├── ambiguous │ │ ├── case1 │ │ │ └── foobar.tpl │ │ └── case2 │ │ │ └── foobar.tpl │ ├── hello_world_test.tpl │ ├── displayfoonofilter.tpl │ ├── phphelloworld.php │ ├── test_plugin_chained_load.tpl │ ├── compilerplugintest.tpl │ ├── smarty.template.tpl │ ├── blockplugintest.tpl │ ├── relative.tpl │ ├── xml.tpl │ ├── functionplugintest.tpl │ ├── relative_notexist.tpl │ ├── relative_sub.tpl │ ├── sub │ │ └── relative.tpl │ ├── test_default_modifier.tpl │ ├── test_inherit_function_tag.tpl │ ├── test_inherit_function_tag6.tpl │ ├── compileblockextends │ │ ├── 028_parent.tpl │ │ ├── 019_parent.tpl │ │ ├── 029_parent.tpl │ │ ├── 028_child.tpl │ │ ├── 016_parent.tpl │ │ ├── 020_include_subtemplate.tpl │ │ ├── 025_parent.tpl │ │ ├── 026_parent.tpl │ │ ├── 001_parent.tpl │ │ ├── 002_parent.tpl │ │ ├── 003_parent.tpl │ │ ├── 004_parent.tpl │ │ ├── 008_parent.tpl │ │ ├── 012_parent.tpl │ │ ├── 014_parent.tpl │ │ ├── 015_parent.tpl │ │ ├── 021_parent.tpl │ │ ├── 017_parent.tpl │ │ ├── 005_parent_shorttag.tpl │ │ ├── 011_parent_nested_include.tpl │ │ ├── 027_parent.tpl │ │ ├── 030_include.tpl │ │ ├── 030_include_2.tpl │ │ ├── 002_child.tpl │ │ ├── 012_child.tpl │ │ ├── 015_child.tpl │ │ ├── 021_child.tpl │ │ ├── 004_child_append.tpl │ │ ├── 006_parent_smartychild.tpl │ │ ├── 012_grandchild.tpl │ │ ├── 021_grandchild.tpl │ │ ├── 008_child_plugin.tpl │ │ ├── 009_parent_nested.tpl │ │ ├── 010_parent_nested.tpl │ │ ├── 006_child_smartychild.tpl │ │ ├── 017_grandchild_nested.tpl │ │ ├── 010_child_nested.tpl │ │ ├── 014_grandchild_smartychild.tpl │ │ ├── 016_grandchild_nested.tpl │ │ ├── 011_grandchild_nested_include.tpl │ │ ├── 020_include_parent.tpl │ │ ├── 020_include_root.tpl │ │ ├── 014_child_smartychild.tpl │ │ ├── 020_include_1.tpl │ │ ├── 020_include_2.tpl │ │ ├── 020_include_3.tpl │ │ ├── 016_child_nested.tpl │ │ ├── 023_child.tpl │ │ ├── 017_child_nested.tpl │ │ ├── 011_child_nested_include.tpl │ │ ├── 023_parent.tpl │ │ ├── 019_child_nested_hide_autoliteral.tpl │ │ ├── 030_grandchild.tpl │ │ ├── 030_parent.tpl │ │ └── 030_child.tpl │ ├── test_register_function.tpl │ ├── test_default_block_script.tpl │ ├── test_default_function_local.tpl │ ├── test_register_block.tpl │ ├── assign.global.tpl │ ├── test_default_function_script.tpl │ ├── test_default_static_modifier.tpl │ ├── test_default_compiler_function_script.tpl │ ├── test_default_function_script_notcachable.tpl │ ├── test_default_modifier_script.tpl │ ├── test_recursive_includes_pass.tpl │ ├── template_function_lib.tpl │ ├── default.tpl │ ├── extendsall.tpl │ ├── extendsall2.tpl │ ├── test_template_function.tpl │ ├── test_template_function_tag7.tpl │ ├── test_template_function_tag6.tpl │ ├── test_define_function_tag.tpl │ ├── test_template_function_tag1.tpl │ ├── test_recursive_includes.tpl │ ├── test_template_function_tag4.tpl │ ├── test_recursive_includes2.tpl │ ├── test_template_function_tag2.tpl │ ├── test_template_function_tag5.tpl │ ├── assign.tpl │ ├── test_template_function_tag3.tpl │ ├── assign.root.tpl │ ├── assign.parent.tpl │ └── whitespace.tpl ├── templates_2 │ ├── hello.tpl │ ├── dirname.tpl │ ├── helloworld.php │ └── ambiguous │ │ └── case1 │ │ └── foobar.tpl ├── templates_3 │ ├── dirname.tpl │ └── extendsall.tpl ├── templates_4 │ ├── dirname.tpl │ └── extendsall.tpl ├── scripts │ ├── test_include_php.php │ ├── script_modifier.php │ ├── script_function_tag.php │ ├── script_compiler_function_tag.php │ ├── script_block_tag.php │ └── script_default_static_modifier.php ├── PHPunitplugins │ ├── function.chain3.php │ ├── compiler.testclose.php │ ├── compiler.test.php │ ├── function.chain1.php │ ├── function.chain2.php │ └── block.testblock.php ├── build-fail.sh ├── AttributeTest.php ├── coverage.xml ├── VariableVariableTest.php ├── TemplateExistsTest.php ├── CompilerPluginTest.php ├── AutoEscapeTest.php ├── XmlTest.php ├── MathTest.php ├── ResourcePlugins │ └── ResourceDB2.php ├── SharedFunctionsTest.php ├── CompileDelimiterTest.php ├── AssignTest.php ├── SmartyVariableTest.php ├── Helpers │ └── Data.php ├── CompileWhileTest.php ├── PluginModifierLowerTest.php ├── Smarty_Test.php ├── PluginModifierUpperTest.php ├── PluginChainedLoadTest.php ├── GetterSetterTest.php ├── PluginModifierCountWordsTest.php ├── PluginStoreLoadBlocksTest.php ├── PluginModifierRegexReplaceTest.php ├── ObjectVariableTest.php ├── FetchTest.php ├── PluginModifierStripTest.php ├── PluginModifierCountCharactersTest.php ├── CompileForTest.php ├── ResourcePluginTest.php ├── CompileFunctionPluginTest.php ├── GenericErrorTest.php ├── CompileCompilerPluginTest.php ├── CompileStripTest.php ├── AssignGlobalTest.php ├── PluginModifierCountSentencesTest.php ├── LiteralTest.php ├── ArrayTest.php ├── RegisterModifierTest.php ├── ClearAssignTest.php ├── FunctionTest.php ├── PluginModifierEscapeTest.php ├── CommentsTest.php ├── ClearAllAssignTest.php ├── SingleQuotedStringTest.php ├── PluginModifierTruncateTest.php ├── DelimiterTest.php ├── CompileFunctionTest.php ├── DefaultAssignScopeTest.php ├── DoubleQuotedStringTest.php ├── CompileForeachTest.php ├── IndexedFileResourceTest.php ├── bootstrap.php └── RegisterCompilerFunctionTest.php ├── logo.png ├── .travis.yml ├── phpunit-hhvm ├── src └── Brainy │ ├── Compiler │ ├── Wrappers │ │ ├── SmartyVarLookupWrapper.php │ │ ├── ArraySafeLookupWrapper.php │ │ ├── ArrayWarnSafeLookupWrapper.php │ │ ├── SubscriptableSafeLookupWrapper.php │ │ ├── SubscriptableWarnSafeLookupWrapper.php │ │ ├── SmartyVarPoisonWrapper.php │ │ ├── SafeLookupWrapper.php │ │ └── StaticWrapper.php │ ├── Constructs │ │ ├── ConstructElse.php │ │ ├── ClosedBaseConstruct.php │ │ ├── ConstructForEachElse.php │ │ ├── ConstructForElse.php │ │ ├── ConstructElseIf.php │ │ ├── ConstructBreak.php │ │ ├── ConstructContinue.php │ │ ├── ConstructWhile.php │ │ ├── ConstructIf.php │ │ ├── ConstructCall.php │ │ ├── ConstructCapture.php │ │ ├── ConstructBlockTerminal.php │ │ ├── ConstructPrintExpression.php │ │ ├── ConstructFunction.php │ │ ├── ConstructAssign.php │ │ └── ConstructInclude.php │ ├── Helpers │ │ ├── Text.php │ │ ├── Expression.php │ │ ├── Tag.php │ │ ├── DoubleQuotedContent.php │ │ ├── ParseTree.php │ │ ├── TemplateBuffer.php │ │ └── DoubleQuoted.php │ └── Decompile.php │ ├── Exceptions │ ├── BrainyStrictModeException.php │ ├── BrainyModifierEnforcementException.php │ ├── SmartyException.php │ └── SmartyCompilerException.php │ ├── plugins │ ├── modifiercompiler.unsafe_noescape.php │ ├── compiler.store.php │ ├── compiler.load.php │ ├── modifiercompiler.upper.php │ ├── modifiercompiler.noprint.php │ ├── shared.escape_special_chars.php │ ├── modifiercompiler.lower.php │ ├── modifiercompiler.string_format.php │ ├── modifiercompiler.cat.php │ ├── compiler.storeclose.php │ ├── modifiercompiler.count_paragraphs.php │ ├── modifiercompiler.count_sentences.php │ ├── modifiercompiler.indent.php │ ├── modifiercompiler.strip_tags.php │ ├── modifiercompiler.count_characters.php │ ├── modifiercompiler.count_words.php │ ├── modifiercompiler.default.php │ ├── modifiercompiler.strip.php │ ├── shared.make_timestamp.php │ ├── modifier.truncate.php │ ├── modifier.replace.php │ ├── modifier.regex_replace.php │ ├── function.counter.php │ ├── modifiercompiler.escape.php │ ├── modifier.escape.php │ ├── modifier.date_format.php │ ├── modifier.wordwrap.php │ └── modifier.capitalize.php │ ├── Templates │ ├── UndefinedVariable.php │ └── CompiledTemplate.php │ ├── Resources │ ├── ResourceRecompiled.php │ ├── ResourceEval.php │ └── ResourceString.php │ └── Runtime │ ├── Loops.php │ ├── PluginLoader.php │ └── Lookups.php ├── .gitignore ├── phpunit.xml ├── composer.json ├── Makefile └── CONTRIBUTING.md /test/templates/dirname.tpl: -------------------------------------------------------------------------------- 1 | templates -------------------------------------------------------------------------------- /test/templates/displayfoo.tpl: -------------------------------------------------------------------------------- 1 | {$foo} -------------------------------------------------------------------------------- /test/templates_2/hello.tpl: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /test/templates/helloworld.tpl: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /test/templates/helloworld2.tpl: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /test/templates/relativity/foo.tpl: -------------------------------------------------------------------------------- 1 | relativity -------------------------------------------------------------------------------- /test/templates_2/dirname.tpl: -------------------------------------------------------------------------------- 1 | templates_2 -------------------------------------------------------------------------------- /test/templates_3/dirname.tpl: -------------------------------------------------------------------------------- 1 | templates_3 -------------------------------------------------------------------------------- /test/templates_4/dirname.tpl: -------------------------------------------------------------------------------- 1 | templates_4 -------------------------------------------------------------------------------- /test/templates/ambiguous/case1/foobar.tpl: -------------------------------------------------------------------------------- 1 | case1 -------------------------------------------------------------------------------- /test/templates/ambiguous/case2/foobar.tpl: -------------------------------------------------------------------------------- 1 | case2 -------------------------------------------------------------------------------- /test/templates/hello_world_test.tpl: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /test/templates/relativity/theory/foo.tpl: -------------------------------------------------------------------------------- 1 | theory -------------------------------------------------------------------------------- /test/templates/displayfoonofilter.tpl: -------------------------------------------------------------------------------- 1 | {$foo nofilter} -------------------------------------------------------------------------------- /test/templates/phphelloworld.php: -------------------------------------------------------------------------------- 1 | php hello world 2 | -------------------------------------------------------------------------------- /test/templates/relativity/relativity.tpl: -------------------------------------------------------------------------------- 1 | relativity -------------------------------------------------------------------------------- /test/templates/relativity/theory/theory.tpl: -------------------------------------------------------------------------------- 1 | theory -------------------------------------------------------------------------------- /test/templates/test_plugin_chained_load.tpl: -------------------------------------------------------------------------------- 1 | {chain1} -------------------------------------------------------------------------------- /test/templates_2/helloworld.php: -------------------------------------------------------------------------------- 1 | php hello world 2 | -------------------------------------------------------------------------------- /test/templates/compilerplugintest.tpl: -------------------------------------------------------------------------------- 1 | {compilerplugin} 2 | -------------------------------------------------------------------------------- /test/templates/relativity/theory/einstein/foo.tpl: -------------------------------------------------------------------------------- 1 | einstein -------------------------------------------------------------------------------- /test/templates/smarty.template.tpl: -------------------------------------------------------------------------------- 1 | {$smarty.template} 2 | -------------------------------------------------------------------------------- /test/templates_2/ambiguous/case1/foobar.tpl: -------------------------------------------------------------------------------- 1 | templates_2 -------------------------------------------------------------------------------- /test/templates/blockplugintest.tpl: -------------------------------------------------------------------------------- 1 | {textformat}abc{/textformat} -------------------------------------------------------------------------------- /test/templates/relative.tpl: -------------------------------------------------------------------------------- 1 | {include file="./helloworld.tpl"} -------------------------------------------------------------------------------- /test/templates/relativity/theory/einstein/einstein.tpl: -------------------------------------------------------------------------------- 1 | einstein -------------------------------------------------------------------------------- /test/templates/xml.tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/box/brainy/master/logo.png -------------------------------------------------------------------------------- /test/templates/functionplugintest.tpl: -------------------------------------------------------------------------------- 1 | {counter start=10 name=tpl} -------------------------------------------------------------------------------- /test/templates/relative_notexist.tpl: -------------------------------------------------------------------------------- 1 | {include file="./hello.tpl"} -------------------------------------------------------------------------------- /test/templates/relative_sub.tpl: -------------------------------------------------------------------------------- 1 | {include file="../helloworld.tpl"} -------------------------------------------------------------------------------- /test/templates/sub/relative.tpl: -------------------------------------------------------------------------------- 1 | {include file="../helloworld.tpl"} -------------------------------------------------------------------------------- /test/templates/test_default_modifier.tpl: -------------------------------------------------------------------------------- 1 | {$foo|mydefaultmodifier} -------------------------------------------------------------------------------- /test/templates/test_inherit_function_tag.tpl: -------------------------------------------------------------------------------- 1 | {call name=functest4} -------------------------------------------------------------------------------- /test/templates/test_inherit_function_tag6.tpl: -------------------------------------------------------------------------------- 1 | {call name=functest6i} -------------------------------------------------------------------------------- /test/scripts/test_include_php.php: -------------------------------------------------------------------------------- 1 | {block 'title'}Default Title{/block} 2 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/011_parent_nested_include.tpl: -------------------------------------------------------------------------------- 1 | {block name='body'}-default-{/block} 2 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/027_parent.tpl: -------------------------------------------------------------------------------- 1 | {block 'b1'}this {$smarty.block.parent} is illegal{/block} -------------------------------------------------------------------------------- /test/templates/default.tpl: -------------------------------------------------------------------------------- 1 | {$foo|default:""} /* should compile something with @silence error suppression */ -------------------------------------------------------------------------------- /test/templates/extendsall.tpl: -------------------------------------------------------------------------------- 1 | {block name="alpha"}templates{/block} 2 | {block name="bravo_2"}templates{/block} -------------------------------------------------------------------------------- /test/templates/extendsall2.tpl: -------------------------------------------------------------------------------- 1 | {block name="alpha"}templates{/block} 2 | {block name="bravo_2"}templates{/block} -------------------------------------------------------------------------------- /test/templates/test_template_function.tpl: -------------------------------------------------------------------------------- 1 | {include file='template_function_lib.tpl'}{call name=template_func1} -------------------------------------------------------------------------------- /test/templates/compileblockextends/030_include.tpl: -------------------------------------------------------------------------------- 1 | {strip} 2 | {$b6}* 3 | {block 'b6'} 4 | include b6* 5 | {/block} -------------------------------------------------------------------------------- /test/templates/compileblockextends/030_include_2.tpl: -------------------------------------------------------------------------------- 1 | {strip} 2 | {block 'b6'} 3 | include b6* 4 | {/block} 5 | -------------------------------------------------------------------------------- /test/templates/test_template_function_tag7.tpl: -------------------------------------------------------------------------------- 1 | {include file='template_function_lib.tpl'}{call name=template_func1} -------------------------------------------------------------------------------- /test/templates/compileblockextends/002_child.tpl: -------------------------------------------------------------------------------- 1 | {extends file='002_parent.tpl'} 2 | {block name='title'}Page Title{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/012_child.tpl: -------------------------------------------------------------------------------- 1 | {extends file='002_parent.tpl'} 2 | {block name='title'}Page Title{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/015_child.tpl: -------------------------------------------------------------------------------- 1 | {extends file='015_parent.tpl'} 2 | {block name='title'}Page Title{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/021_child.tpl: -------------------------------------------------------------------------------- 1 | {extends file='021_parent.tpl'} 2 | {block name='title'}Page Title{/block} 3 | -------------------------------------------------------------------------------- /test/PHPunitplugins/function.chain3.php: -------------------------------------------------------------------------------- 1 | {$smarty.block.child}< included{/block} 2 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/012_grandchild.tpl: -------------------------------------------------------------------------------- 1 | {extends file='012_child.tpl'} 2 | {block name='title'}Grandchild Page Title{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/021_grandchild.tpl: -------------------------------------------------------------------------------- 1 | {extends file='021_child.tpl'} 2 | {block name='title'}Grandchild Page Title{/block} 3 | -------------------------------------------------------------------------------- /test/templates/test_define_function_tag.tpl: -------------------------------------------------------------------------------- 1 | {function name=functest6i loop=0}{$loop}{if $loop < 5}{call name=functest6i loop=$loop+1}{/if}{/function} -------------------------------------------------------------------------------- /test/templates/compileblockextends/008_child_plugin.tpl: -------------------------------------------------------------------------------- 1 | {extends file='008_parent.tpl'} 2 | {block name='title'}{'escaped '|escape}{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/009_parent_nested.tpl: -------------------------------------------------------------------------------- 1 | {block name='title'}Title with {block name='title_content'}-default-{/block} here{/block} 2 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/010_parent_nested.tpl: -------------------------------------------------------------------------------- 1 | {block name='title'}Title with {block name='title_content'}-default-{/block} here{/block} 2 | -------------------------------------------------------------------------------- /test/templates/test_template_function_tag1.tpl: -------------------------------------------------------------------------------- 1 | {function name=functest default='default'}{$default} {$param}{/function}{call name=functest param='param'} -------------------------------------------------------------------------------- /test/templates/compileblockextends/006_child_smartychild.tpl: -------------------------------------------------------------------------------- 1 | {extends file='006_parent_smartychild.tpl'} 2 | {block name='title'}child text{/block} 3 | 4 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/017_grandchild_nested.tpl: -------------------------------------------------------------------------------- 1 | {extends file='017_child_nested.tpl'} 2 | {block name='content2'}-grandchild content-{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/010_child_nested.tpl: -------------------------------------------------------------------------------- 1 | {extends file='010_parent_nested.tpl'} 2 | {block name='title_content'}-content from child-{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/014_grandchild_smartychild.tpl: -------------------------------------------------------------------------------- 1 | {extends file='014_child_smartychild.tpl'} 2 | {block name='title'}grandchild content{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/016_grandchild_nested.tpl: -------------------------------------------------------------------------------- 1 | {extends file='016_child_nested.tpl'} 2 | {block name='title_content'}-grandchild content-{/block} 3 | -------------------------------------------------------------------------------- /test/templates_4/extendsall.tpl: -------------------------------------------------------------------------------- 1 | {block name="alpha"}templates_4{/block} 2 | {block name="bravo"}templates_4{/block} 3 | {block name="charlie"}templates_4{/block} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.4 4 | - 5.5 5 | - 5.6 6 | install: composer install 7 | script: ./test/build-fail.sh && make test 8 | sudo: false 9 | -------------------------------------------------------------------------------- /test/scripts/script_function_tag.php: -------------------------------------------------------------------------------- 1 | 2 | {if $foo < 3}{include 'test_recursive_includes.tpl' foo=$foo+1}{/if} 3 | after {$foo} {$bar}
4 | -------------------------------------------------------------------------------- /test/templates/test_template_function_tag4.tpl: -------------------------------------------------------------------------------- 1 | {function name=functest4 loop=0}{$loop}{if $loop < 5}{call name=functest4 loop=$loop+1}{/if}{/function}{call name=functest4} -------------------------------------------------------------------------------- /test/templates/compileblockextends/020_include_parent.tpl: -------------------------------------------------------------------------------- 1 | {block name="p"} {/block} 2 | {block name='dummy'} 3 | {include file='020_include_subtemplate.tpl'} 4 | {/block} 5 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/020_include_root.tpl: -------------------------------------------------------------------------------- 1 | {include file='020_include_1.tpl'} 2 | {include file='020_include_2.tpl'} 3 | {include file='020_include_3.tpl'} 4 | 5 | -------------------------------------------------------------------------------- /test/templates/test_recursive_includes2.tpl: -------------------------------------------------------------------------------- 1 | before {$foo} {$bar}
2 | {if $foo < 4}{include 'test_recursive_includes_pass.tpl' foo=$foo+1}{/if} 3 | after {$foo} {$bar}
4 | -------------------------------------------------------------------------------- /test/PHPunitplugins/compiler.testclose.php: -------------------------------------------------------------------------------- 1 | _tag_stack); 5 | return ""; 6 | } 7 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/014_child_smartychild.tpl: -------------------------------------------------------------------------------- 1 | {extends file='014_parent.tpl'} 2 | {block name='title' force}child title with - {$smarty.block.child} - here{/block} 3 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/020_include_1.tpl: -------------------------------------------------------------------------------- 1 | {extends file='020_include_parent.tpl'} 2 | 3 | {block name="p"}page 1
{/block} 4 | 5 | {block name="b"}block 1
{/block} -------------------------------------------------------------------------------- /test/templates/compileblockextends/020_include_2.tpl: -------------------------------------------------------------------------------- 1 | {extends file='020_include_parent.tpl'} 2 | 3 | {block name="p"}page 2
{/block} 4 | 5 | {block name="b"}block 2
{/block} -------------------------------------------------------------------------------- /test/templates/compileblockextends/020_include_3.tpl: -------------------------------------------------------------------------------- 1 | {extends file='020_include_parent.tpl'} 2 | 3 | {block name="p"}page 3
{/block} 4 | 5 | {block name="b"}block 3
{/block} -------------------------------------------------------------------------------- /test/templates/compileblockextends/016_child_nested.tpl: -------------------------------------------------------------------------------- 1 | {extends file='016_parent.tpl'} 2 | {block name='title'}child title with {block name='title_content'}-default-{/block} here{/block} 3 | -------------------------------------------------------------------------------- /test/templates/test_template_function_tag2.tpl: -------------------------------------------------------------------------------- 1 | {function name=functest2 default='default'}{$default} {$param}{/function}{call name=functest2 param='param'} {call name=functest2 param='param2'} -------------------------------------------------------------------------------- /test/templates/test_template_function_tag5.tpl: -------------------------------------------------------------------------------- 1 | {function name=functest4 loop=0}{$loop}{if $loop < 5}{call name=functest4 loop=$loop+1}{/if}{/function}{include file='test_inherit_function_tag.tpl'} -------------------------------------------------------------------------------- /test/templates/assign.tpl: -------------------------------------------------------------------------------- 1 | {assign var="local" value="local"} {$local|default:"no-local"} {include "assign.parent.tpl"} {$parent|default:"no-parent"} {$root|default:"no-root"} {$global|default:"no-global"} -------------------------------------------------------------------------------- /test/templates_3/extendsall.tpl: -------------------------------------------------------------------------------- 1 | {block name="alpha"}templates_3{/block} 2 | {block name="bravo"} 3 | {block name="bravo_1"}templates_3{/block} 4 | {block name="bravo_2"}templates_3{/block} 5 | {/block} -------------------------------------------------------------------------------- /test/scripts/script_compiler_function_tag.php: -------------------------------------------------------------------------------- 1 | 5 | Demo 6 | 7 | {/block} 8 | {/strip} 9 | -------------------------------------------------------------------------------- /test/templates/test_template_function_tag3.tpl: -------------------------------------------------------------------------------- 1 | {function name=functest3 default='default'}{$default} {$param}{/function}{call name=functest3 param='param' default='overwrite'} {call name=functest3 param='param2'} -------------------------------------------------------------------------------- /test/PHPunitplugins/compiler.test.php: -------------------------------------------------------------------------------- 1 | _tag_stack, array('test', null)); 5 | return "echo 'test output';\n"; 6 | } 7 | -------------------------------------------------------------------------------- /test/scripts/script_block_tag.php: -------------------------------------------------------------------------------- 1 | 3 | {block name="strip"} 4 |
5 | Content is stripped 6 |
7 | {/block} 8 | 9 | {/strip} -------------------------------------------------------------------------------- /test/templates/compileblockextends/019_child_nested_hide_autoliteral.tpl: -------------------------------------------------------------------------------- 1 | { extends file="019_parent.tpl" } 2 | 3 | { block name="index" } 4 | { block name="test2" } 5 | nested block. 6 | { $smarty.block.child } 7 | { /block } 8 | { /block } 9 | -------------------------------------------------------------------------------- /test/PHPunitplugins/function.chain1.php: -------------------------------------------------------------------------------- 1 | smarty); 4 | return smarty_function_chain2($params, $tpl); 5 | } 6 | -------------------------------------------------------------------------------- /test/PHPunitplugins/function.chain2.php: -------------------------------------------------------------------------------- 1 | smarty); 4 | return smarty_function_chain3($params,$tpl); 5 | } 6 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/SmartyVarLookupWrapper.php: -------------------------------------------------------------------------------- 1 | _tag_stack, array('store', $params['to'])); 12 | return "ob_start();\n"; 13 | } 14 | -------------------------------------------------------------------------------- /src/Brainy/Exceptions/BrainyModifierEnforcementException.php: -------------------------------------------------------------------------------- 1 | expectException(\Box\Brainy\Exceptions\SmartyCompilerException::class); 11 | $this->expectExceptionMessage('missing "var" attribute'); 12 | $this->smarty->display('eval:{assign value=1}'); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/Brainy/Templates/UndefinedVariable.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ../../distribution/libs 6 | ../../distribution/demo 7 | 8 | ../../distribution/demo/index.php 9 | ../../distribution/demo/templates_c 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/ArraySafeLookupWrapper.php: -------------------------------------------------------------------------------- 1 | safeVersion . ', ' . $this->member . ')'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/ArrayWarnSafeLookupWrapper.php: -------------------------------------------------------------------------------- 1 | safeVersion . ', ' . $this->member . ')'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/VariableVariableTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{$foo=\'bar\'}{$bar=123}{${$foo}}'); 16 | $this->assertEquals('123', $this->smarty->fetch($tpl)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/SubscriptableSafeLookupWrapper.php: -------------------------------------------------------------------------------- 1 | safeVersion . ', ' . $this->member . ')'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Brainy/plugins/compiler.load.php: -------------------------------------------------------------------------------- 1 | tpl_vars['smarty']['ls_loadables'][{$params['from']}]; 18 | OUT; 19 | } 20 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/SubscriptableWarnSafeLookupWrapper.php: -------------------------------------------------------------------------------- 1 | safeVersion . ', ' . $this->member . ')'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | tags 4 | TAGS 5 | *.swp 6 | *.egg-info 7 | .emacs.desktop 8 | .#* 9 | dist 10 | *~ 11 | .buildout 12 | .emacs.d/.autosaves 13 | .emacs.d/.emacs-places 14 | .emacs.d/tramp 15 | .zsh/cache 16 | *.elc 17 | .emacs.d/history 18 | .emacs.d/site/nxhtml 19 | .virtualenvs 20 | .ipython 21 | *.stackdump 22 | node_modules/ 23 | Thumbs.db 24 | desktop.ini 25 | *.sublime-project 26 | cache/ 27 | *.tpl.php 28 | *.out 29 | coverage/ 30 | test/compiled 31 | vendor/ 32 | *.dat 33 | 34 | .phpunit.result.cache 35 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | test/ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/TemplateExistsTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->smarty->templateExists('helloworld.tpl')); 16 | } 17 | 18 | public function testSmartyTemplateNotExists() { 19 | $this->assertFalse($this->smarty->templateExists('notthere.tpl')); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/CompilerPluginTest.php: -------------------------------------------------------------------------------- 1 | smarty->addPluginsDir(dirname(__FILE__) . "/PHPunitplugins/"); 19 | $this->assertEquals('test output', $this->smarty->fetch('eval:{test data="test output"}{/test}')); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.upper.php: -------------------------------------------------------------------------------- 1 | 7 | * Name: lower
8 | * Purpose: convert string to uppercase 9 | * 10 | * @link http://smarty.php.net/manual/en/language.modifier.upper.php lower (Smarty online manual) 11 | * @author Uwe Tews 12 | * @param array $params parameters 13 | * @return string with compiled code 14 | */ 15 | function smarty_modifiercompiler_upper($params, $compiler) 16 | { 17 | return 'mb_strtoupper(' . $params[0] . ', \'UTF-8\')'; 18 | } 19 | -------------------------------------------------------------------------------- /src/Brainy/Exceptions/SmartyException.php: -------------------------------------------------------------------------------- 1 | Smarty: ' . (self::$escape ? htmlentities($this->message) : $this->message) . ' <-- '; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.noprint.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: noprint
14 | * Purpose: return an empty string 15 | * 16 | * @author Uwe Tews 17 | * @param array $params parameters 18 | * @return string with compiled code 19 | */ 20 | function smarty_modifiercompiler_noprint($params, $compiler) 21 | { 22 | $compiler->assertIsNotStrict('noprint is not allowed in strict mode'); 23 | return "''"; 24 | } 25 | -------------------------------------------------------------------------------- /test/AutoEscapeTest.php: -------------------------------------------------------------------------------- 1 | smarty->escape_html = true; 17 | } 18 | 19 | public function testAutoEscape() { 20 | $tpl = $this->smarty->createTemplate('eval:{$foo}'); 21 | $tpl->assign('foo',''); 22 | $this->assertEquals("<a@b.c>", $this->smarty->fetch($tpl)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructElse.php: -------------------------------------------------------------------------------- 1 | tag handling 4 | * 5 | * @package PHPunit 6 | * @author Uwe Tews 7 | */ 8 | 9 | namespace Box\Brainy\Tests; 10 | 11 | 12 | class XmlTest extends Smarty_TestCase 13 | { 14 | public function setup(): void { 15 | parent::setUp(); 16 | $this->smarty->force_compile = true; 17 | } 18 | 19 | /** 20 | * test standard xml 21 | */ 22 | public function testXml() { 23 | $tpl = $this->smarty->createTemplate('xml.tpl'); 24 | $this->assertEquals('', $this->smarty->fetch($tpl)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/MathTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, $this->smarty->fetch('eval:' . $code)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/SmartyVarPoisonWrapper.php: -------------------------------------------------------------------------------- 1 | type = $type; 16 | } 17 | 18 | /** 19 | * @return string 20 | */ 21 | public function __toString() 22 | { 23 | throw new \Box\Brainy\Exceptions\SmartyCompilerException( 24 | '"$smarty.foreach" or "$smarty.capture" were used directly. This is not allowed.' 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/ResourcePlugins/ResourceDB2.php: -------------------------------------------------------------------------------- 1 | filepath = 'db2:'; 14 | $source->uid = sha1($source->name); 15 | $source->timestamp = time(); 16 | $source->exists = true; 17 | } 18 | 19 | public function getContent(TemplateSource $source) 20 | { 21 | return '{$x="hello world"}{$x}'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ClosedBaseConstruct.php: -------------------------------------------------------------------------------- 1 | 13 | * Purpose: used by other smarty functions to escape 14 | * special chars except for already escaped ones 15 | * 16 | * @author Monte Ohrt 17 | * @param string $string text that should by escaped 18 | * @return string 19 | */ 20 | function smarty_function_escape_special_chars($string) 21 | { 22 | return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false); 23 | } 24 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/030_parent.tpl: -------------------------------------------------------------------------------- 1 | {strip} 2 | {block 'b1'} 3 | parent b1 {$b1}* 4 | {block 'b2'} 5 | parent b2* 6 | {/block} 7 | {block 'b3'} 8 | parent b3* 9 | {/block} 10 | {/block}
11 | {block 'b4'} 12 | parent b4* 13 | {/block}
14 | {block 'b5'} 15 | parent b5* 16 | {/block}
17 | {block 'b61'} 18 | parent b61* 19 | {/block}
20 | {block 'b62'} 21 | parent b62* 22 | {/block}
23 | {block 'b63'} 24 | parent b63* 25 | {/block}
26 | {block 'b64'} 27 | parent b64* 28 | {/block}
29 | parent include {include '030_include.tpl' inline}
30 | parent include2 {include '030_include_2.tpl' inline}

31 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructForElse.php: -------------------------------------------------------------------------------- 1 | assertEquals('hello<world ©', smarty_function_escape_special_chars('helloassertEquals('ö€', smarty_function_escape_special_chars('ö€')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/CompileDelimiterTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:x{ldelim}x'); 19 | $this->assertEquals('x{x', $this->smarty->fetch($tpl)); 20 | } 21 | public function testRightDelimiter() { 22 | $tpl = $this->smarty->createTemplate('eval:x{rdelim}x'); 23 | $this->assertEquals('x}x', $this->smarty->fetch($tpl)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/templates/compileblockextends/030_child.tpl: -------------------------------------------------------------------------------- 1 | {extends '030_parent.tpl'} 2 | {strip} 3 | {block 'b3'} 4 | child b3}* 5 | {/block} 6 | {block 'b4'} 7 | child b4 {$b4}* 8 | {$smarty.block.child}* 9 | {/block} 10 | {block 'b5'} 11 | child b5 {$b5}* 12 | {$smarty.block.child}* 13 | {/block} 14 | {block 'b61'} 15 | child b61 {$b6}* 16 | include 61 {include '030_include.tpl'} 17 | {/block} 18 | {block 'b62'} 19 | child b62 {$b6}* 20 | include 62 {include '030_include.tpl'} 21 | {/block} 22 | {block 'b63'} 23 | child b63 {$b6}* 24 | {include '030_include_2.tpl'} 25 | {/block} 26 | {block 'b64'} 27 | child b64 {$b6}* 28 | include b64 {include '030_include.tpl'} 29 | {/block} 30 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.lower.php: -------------------------------------------------------------------------------- 1 | 12 | * Name: lower
13 | * Purpose: convert string to lowercase 14 | * 15 | * @link http://www.smarty.net/manual/en/language.modifier.lower.php lower (Smarty online manual) 16 | * @author Monte Ohrt 17 | * @author Uwe Tews 18 | * @param array $params parameters 19 | * @return string with compiled code 20 | */ 21 | 22 | function smarty_modifiercompiler_lower($params, $compiler) 23 | { 24 | return 'mb_strtolower(' . $params[0] . ', \'UTF-8\')' ; 25 | } 26 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.string_format.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: string_format
14 | * Purpose: format strings via sprintf 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.string.format.php string_format (Smarty online manual) 17 | * @author Uwe Tews 18 | * @param array $params parameters 19 | * @return string with compiled code 20 | */ 21 | function smarty_modifiercompiler_string_format($params, $compiler) 22 | { 23 | return 'sprintf(' . $params[1] . ',' . $params[0] . ')'; 24 | } 25 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructElseIf.php: -------------------------------------------------------------------------------- 1 | smarty->assign('foo', 'bar'); 19 | $this->assertEquals('bar', $this->smarty->fetch('eval:{$foo}')); 20 | } 21 | /** 22 | * test assign array of variables 23 | */ 24 | public function testArrayAssign() { 25 | $this->smarty->assign(array('foo'=>'bar', 'foo2'=>'bar2')); 26 | $this->assertEquals('bar bar2', $this->smarty->fetch('string:{$foo} {$foo2}')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/Text.php: -------------------------------------------------------------------------------- 1 | data = $data; 13 | } 14 | 15 | /** 16 | * @return string 17 | */ 18 | public function toInlineData() 19 | { 20 | return $this->data; 21 | } 22 | 23 | /** 24 | * @return strint text 25 | */ 26 | public function toSmartyPHP() 27 | { 28 | return var_export($this->data, true); 29 | } 30 | 31 | /** 32 | * @return bool 33 | */ 34 | public function canCombineInlineData() 35 | { 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Brainy/Exceptions/SmartyCompilerException.php: -------------------------------------------------------------------------------- 1 | Smarty Compiler: ' . $this->message . ' <-- '; 13 | } 14 | public $line; 15 | /** 16 | * The template source snippet relating to the error 17 | * @var string|null 18 | */ 19 | public $source = null; 20 | /** 21 | * The raw text of the error message 22 | * @var string|null 23 | */ 24 | public $desc = null; 25 | /** 26 | * The resource identifier or template name 27 | * @var string|null 28 | */ 29 | public $template = null; 30 | } 31 | -------------------------------------------------------------------------------- /test/SmartyVariableTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(\Box\Brainy\Brainy::SMARTY_VERSION, $this->smarty->fetch('eval:{$smarty.version}')); 10 | } 11 | public function testTemplate() { 12 | $this->assertStringContainsString('smarty.template.tpl', $this->smarty->fetch('smarty.template.tpl')); 13 | } 14 | public function testNow() { 15 | $out = $this->smarty->fetch('eval:{$smarty.now}'); 16 | $now = time(); 17 | $this->assertTrue( 18 | (int) $out === $now || 19 | (int) $out + 1 === $now || 20 | (int) $out - 1 === $now 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.cat.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: cat
14 | * Date: Feb 24, 2003
15 | * Purpose: catenate a value to a variable
16 | * Input: string to catenate
17 | * Example: {$var|cat:"foo"} 18 | * 19 | * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat 20 | * (Smarty online manual) 21 | * @author Uwe Tews 22 | * @param array $params parameters 23 | * @return string with compiled code 24 | */ 25 | function smarty_modifiercompiler_cat($params, $compiler) 26 | { 27 | return '(' . implode(').(', $params) . ')'; 28 | } 29 | -------------------------------------------------------------------------------- /test/Helpers/Data.php: -------------------------------------------------------------------------------- 1 | parent = $_parent; 26 | $this->smarty = $smarty; 27 | } 28 | 29 | /** 30 | * Hook to allow subclasses to initialize their data structures. 31 | * @return void 32 | */ 33 | protected function setUpTemplateData() 34 | {} 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/Brainy/plugins/compiler.storeclose.php: -------------------------------------------------------------------------------- 1 | _tag_stack) === 0) { 12 | $compiler->trigger_template_error('unexpected closing tag', $compiler->lex->taglineno); 13 | } 14 | 15 | // get stacked info 16 | list($openTag, $to) = array_pop($compiler->_tag_stack); 17 | 18 | if ($openTag !== 'store') { 19 | $compiler->trigger_template_error('Got {/' . $openTag . '}, but expected {/store}', $compiler->lex->taglineno); 20 | } 21 | 22 | return "\$_smarty_tpl->tpl_vars['smarty']['ls_loadables'][$to] = ob_get_clean();\n"; 23 | } 24 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.count_paragraphs.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: count_paragraphs
14 | * Purpose: count the number of paragraphs in a text 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php 17 | * count_paragraphs (Smarty online manual) 18 | * @author Uwe Tews 19 | * @param array $params parameters 20 | * @return string with compiled code 21 | */ 22 | function smarty_modifiercompiler_count_paragraphs($params, $compiler) 23 | { 24 | // count \r or \n characters 25 | return '(preg_match_all(\'#[\r\n]+#\', ' . $params[0] . ', $tmp)+1)'; 26 | } 27 | -------------------------------------------------------------------------------- /test/CompileWhileTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{$x=0}{while $x<10}{$x}{$x=$x+1}{/while}'); 19 | $this->assertEquals("0123456789", $this->smarty->fetch($tpl)); 20 | } 21 | 22 | /** 23 | * test {while 'statement'} tag 24 | */ 25 | public function testWhileStatement() { 26 | $tpl = $this->smarty->createTemplate('eval:{$y=4}{while $y}{$y}{$y=$y-1}{/while}'); 27 | $this->assertEquals("4321", $this->smarty->fetch($tpl)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/PHPunitplugins/block.testblock.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"Two Convicts Evade Noose, Jury Hung."|lower}'); 17 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 18 | } 19 | 20 | public function testUmlauts() { 21 | $result = "two convicts eväde nööse, jury hung."; 22 | $tpl = $this->smarty->createTemplate('eval:{"Two Convicts Eväde NöÖse, Jury Hung."|lower}'); 23 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.count_sentences.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: count_sentences 14 | * Purpose: count the number of sentences in a text 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.count.sentences.php 17 | * count_sentences (Smarty online manual) 18 | * @author Uwe Tews 19 | * @param array $params parameters 20 | * @return string with compiled code 21 | */ 22 | function smarty_modifiercompiler_count_sentences($params, $compiler) 23 | { 24 | // find periods, question marks, exclamation marks with a word before but not after. 25 | return 'preg_match_all("#\S[\.\?\!](\W|$)#Su' . '", ' . $params[0] . ', $tmp)'; 26 | } 27 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.indent.php: -------------------------------------------------------------------------------- 1 | 12 | * Name: indent
13 | * Purpose: indent lines of text 14 | * 15 | * @link http://www.smarty.net/manual/en/language.modifier.indent.php indent (Smarty online manual) 16 | * @author Uwe Tews 17 | * @param array $params parameters 18 | * @return string with compiled code 19 | */ 20 | 21 | function smarty_modifiercompiler_indent($params, $compiler) 22 | { 23 | if (!isset($params[1])) { 24 | $params[1] = 4; 25 | } 26 | if (!isset($params[2])) { 27 | $params[2] = "' '"; 28 | } 29 | 30 | return 'preg_replace(\'!^!m\', str_repeat(' . $params[2] . ',' . $params[1] . '),' . $params[0] . ')'; 31 | } 32 | -------------------------------------------------------------------------------- /src/Brainy/Resources/ResourceRecompiled.php: -------------------------------------------------------------------------------- 1 | filepath = false; 27 | $compiled->timestamp = false; 28 | $compiled->exists = false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.strip_tags.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: strip_tags
14 | * Purpose: strip html tags from text 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.strip.tags.php strip_tags (Smarty online manual) 17 | * @author Uwe Tews 18 | * @param array $params parameters 19 | * @return string with compiled code 20 | */ 21 | function smarty_modifiercompiler_strip_tags($params, $compiler) 22 | { 23 | if (!isset($params[1]) || $params[1] === true || trim($params[1], '"') == 'true') { 24 | return "preg_replace('!<[^>]*?>!', ' ', {$params[0]})"; 25 | } else { 26 | return 'strip_tags(' . $params[0] . ')'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Smarty_Test.php: -------------------------------------------------------------------------------- 1 | setUpInstance($smarty); 11 | $smarty->fetch('helloworld.tpl'); 12 | $this->assertEquals('helloworld.tpl', $smarty->fetched); 13 | } 14 | public function test_fetchedTemplate_include() { 15 | $smarty = new ExtendedSmarty(); 16 | $this->setUpInstance($smarty); 17 | $smarty->fetch('eval:{include file="helloworld2.tpl"}'); 18 | $this->assertEquals('helloworld2.tpl', $smarty->fetched); 19 | } 20 | } 21 | 22 | 23 | class ExtendedSmarty extends \Box\Brainy\Brainy 24 | { 25 | public $fetched = null; 26 | 27 | public function fetchedTemplate($name) 28 | { 29 | $this->fetched = $name; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.count_characters.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: count_characteres
14 | * Purpose: count the number of characters in a text 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual) 17 | * @author Uwe Tews 18 | * @param array $params parameters 19 | * @return string with compiled code 20 | */ 21 | function smarty_modifiercompiler_count_characters($params, $compiler) 22 | { 23 | if (!isset($params[1]) || $params[1] != 'true') { 24 | return 'preg_match_all(\'/[^\s]/u' . '\',' . $params[0] . ', $tmp)'; 25 | } 26 | return 'mb_strlen(' . $params[0] . ', \'UTF-8\')'; 27 | } 28 | -------------------------------------------------------------------------------- /test/PluginModifierUpperTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"If Strike isn\'t Settled Quickly it may Last a While."|upper}'); 17 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 18 | } 19 | 20 | public function testUmlauts() { 21 | $result = "IF STRIKE ISN'T SÄTTLED ÜQUICKLY IT MAY LAST A WHILE."; 22 | $tpl = $this->smarty->createTemplate('eval:{"If Strike isn\'t Sättled ÜQuickly it may Last a While."|upper}'); 23 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /test/PluginChainedLoadTest.php: -------------------------------------------------------------------------------- 1 | smarty->addPluginsDir(dirname(__FILE__) . "/PHPunitplugins/"); 16 | $this->assertStringContainsString('from chain3', $this->smarty->fetch('test_plugin_chained_load.tpl')); 17 | } 18 | public function testDeferredChainedLoad() { 19 | $this->smarty->addPluginsDir(dirname(__FILE__) . "/PHPunitplugins/"); 20 | $tpl1 = $this->smarty->createTemplate('string:{chain1}'); 21 | $tpl1->fetch(); 22 | $output = $tpl1->fetch(); // should not throw 23 | $this->assertStringContainsString('from chain3', $output); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructBreak.php: -------------------------------------------------------------------------------- 1 | assertIsNotStrict('Break shorthand is not allowed in strict mode. Use the levels="" attribute instead.'); 18 | $levels = $args[0]; 19 | } 20 | 21 | if ($levels) { 22 | return "break $levels;\n"; 23 | } 24 | 25 | return "break;\n"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructContinue.php: -------------------------------------------------------------------------------- 1 | assertIsNotStrict('Continue shorthand is not allowed in strict mode. Use the levels="" attribute instead.'); 18 | $levels = $args[0]; 19 | } 20 | 21 | if ($levels) { 22 | return "continue $levels;\n"; 23 | } 24 | 25 | return "continue;\n"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.count_words.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: count_words
14 | * Purpose: count the number of words in a text 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.count.words.php count_words (Smarty online manual) 17 | * @author Uwe Tews 18 | * @param array $params parameters 19 | * @return string with compiled code 20 | */ 21 | function smarty_modifiercompiler_count_words($params, $compiler) 22 | { 23 | // return 'preg_match_all(\'#[\w\pL]+#u' . '\', ' . $params[0] . ', $tmp)'; 24 | // expression taken from http://de.php.net/manual/en/function.str-word-count.php#85592 25 | return 'preg_match_all(\'/\p{L}[\p{L}\p{Mn}\p{Pd}\\\'\x{2019}]*/u' . '\', ' . $params[0] . ', $tmp)'; 26 | } 27 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/SafeLookupWrapper.php: -------------------------------------------------------------------------------- 1 | unsafeVersion = $unsafe; 19 | $this->safeVersion = $safe; 20 | $this->member = $member; 21 | } 22 | 23 | /** 24 | * @return string 25 | */ 26 | public function getUnsafeRecursive() 27 | { 28 | return $this->unsafeVersion . '[' . $this->member . ']'; 29 | } 30 | 31 | /** 32 | * @return string 33 | */ 34 | public function getUnsafe() 35 | { 36 | return $this->safeVersion . '[' . $this->member . ']'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.default.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: default
14 | * Purpose: designate default value for empty variables 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.default.php default (Smarty online manual) 17 | * @author Uwe Tews 18 | * @param array $params parameters 19 | * @return string with compiled code 20 | */ 21 | function smarty_modifiercompiler_default($params, $compiler) 22 | { 23 | $output = $params[0]; 24 | if (!isset($params[1])) { 25 | $params[1] = "''"; 26 | } 27 | 28 | array_shift($params); 29 | foreach ($params as $param) { 30 | $output = '(($tmp = ' . $output . ')===null||$tmp===\'\' ? ' . $param . ' : $tmp)'; 31 | } 32 | 33 | return $output; 34 | } 35 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.strip.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: strip
14 | * Purpose: Replace all repeated spaces, newlines, tabs 15 | * with a single space or supplied replacement string.
16 | * Example: {$var|strip} {$var|strip:" "}
17 | * Date: September 25th, 2002 18 | * 19 | * @link http://www.smarty.net/manual/en/language.modifier.strip.php strip (Smarty online manual) 20 | * @author Uwe Tews 21 | * @param array $params parameters 22 | * @return string with compiled code 23 | */ 24 | 25 | function smarty_modifiercompiler_strip($params, $compiler) 26 | { 27 | if (!isset($params[1])) { 28 | $params[1] = "' '"; 29 | } 30 | 31 | return "preg_replace('!\s+!u" . "', {$params[1]}, {$params[0]})"; 32 | } 33 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/Expression.php: -------------------------------------------------------------------------------- 1 | data = $data; 15 | } 16 | 17 | /** 18 | * @return string 19 | */ 20 | public function toInlineData() 21 | { 22 | throw new \Box\Brainy\Exceptions\SmartyException('PHP Expression cast to inline template data'); 23 | } 24 | 25 | /** 26 | * Return buffer content in parentheses 27 | * 28 | * @return string content 29 | */ 30 | public function toSmartyPHP() 31 | { 32 | return sprintf("(%s)", $this->data); 33 | } 34 | 35 | /** 36 | * @return bool 37 | */ 38 | public function canCombineInlineData() 39 | { 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/Tag.php: -------------------------------------------------------------------------------- 1 | data = $data; 17 | } 18 | 19 | /** 20 | * @return string 21 | */ 22 | public function toInlineData() 23 | { 24 | throw new \Box\Brainy\Exceptions\SmartyException('Brainy tag cast to inline template data'); 25 | } 26 | 27 | /** 28 | * Return buffer content 29 | * 30 | * @return string content 31 | */ 32 | public function toSmartyPHP() 33 | { 34 | return $this->data; 35 | } 36 | 37 | /** 38 | * @return bool 39 | */ 40 | public function canCombineInlineData() 41 | { 42 | return false; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/GetterSetterTest.php: -------------------------------------------------------------------------------- 1 | smarty->setLeftDelimiter('<{'); 19 | $this->smarty->setRightDelimiter('}>'); 20 | $this->assertEquals('<{', $this->smarty->left_delimiter); 21 | $this->assertEquals('}>', $this->smarty->right_delimiter); 22 | } 23 | /** 24 | * test getter on Smarty object 25 | */ 26 | public function testSmartyGetter() { 27 | $this->smarty->setLeftDelimiter('<{'); 28 | $this->smarty->setRightDelimiter('}>'); 29 | $this->assertEquals('<{', $this->smarty->getLeftDelimiter()); 30 | $this->assertEquals('}>', $this->smarty->getRightDelimiter()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/DoubleQuotedContent.php: -------------------------------------------------------------------------------- 1 | data = stripcslashes($data); 16 | } 17 | 18 | /** 19 | * @return string 20 | */ 21 | public function toInlineData() 22 | { 23 | // echo $this->data, stripslashes($this->data), var_export($this->data, true);exit; 24 | return $this->data; 25 | } 26 | 27 | /** 28 | * @return string doubled quoted string 29 | */ 30 | public function toSmartyPHP() 31 | { 32 | return var_export($this->data, true); 33 | } 34 | 35 | /** 36 | * @return bool 37 | */ 38 | public function canCombineInlineData() 39 | { 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/PluginModifierCountWordsTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"Dealers Will Hear Car Talk at Noon."|count_words}'); 17 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 18 | } 19 | 20 | public function testDashes() { 21 | $result = "7"; 22 | $tpl = $this->smarty->createTemplate('eval:{"Smalltime-Dealers Will Hear Car Talk at Noon."|count_words}'); 23 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 24 | } 25 | 26 | public function testUmlauts() { 27 | $result = "7"; 28 | $tpl = $this->smarty->createTemplate('eval:{"Dealers Will Hear Cär Talk at Nöön."|count_words}'); 29 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /test/PluginStoreLoadBlocksTest.php: -------------------------------------------------------------------------------- 1 | smarty->assign('varname', 'poison'); 12 | } 13 | 14 | public function testStoreLoad() 15 | { 16 | $this->expectOutputString('foo bar'); 17 | $this->smarty->display('eval:{store to="varname"}foo bar{/store}{load from="varname"}'); 18 | } 19 | 20 | public function testStoreLoadDoesNotOutput() 21 | { 22 | $this->expectOutputString(''); 23 | $this->smarty->display('eval:{store to="varname"}foo bar{/store}'); 24 | } 25 | 26 | public function testStoreLoadScope() 27 | { 28 | $this->expectOutputString('foo bar'); 29 | $this->smarty->display('eval:{store to="varname"}foo bar{/store}{assign var="varname" value="poop"}{load from="varname"}'); 30 | $this->assertEquals('poison', $this->smarty->getTemplateVars('varname')); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructWhile.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"Infertility unlikely to\nbe passed on, experts say."|regex_replace:"/[\r\t\n]/":" "}'); 16 | $this->assertEquals("Infertility unlikely to be passed on, experts say.", $this->smarty->fetch($tpl)); 17 | } 18 | 19 | public function testUmlauts() { 20 | $tpl = $this->smarty->createTemplate('eval:{"Infertility unlikely tö\näe passed on, experts say."|regex_replace:"/[\r\t\n]/u":" "}'); 21 | $this->assertEquals("Infertility unlikely tö äe passed on, experts say.", $this->smarty->fetch($tpl)); 22 | 23 | $tpl = $this->smarty->createTemplate('eval:{"Infertility unlikely tä be passed on, experts say."|regex_replace:"/[ä]/ue":"ae"}'); 24 | $this->assertEquals("Infertility unlikely tae be passed on, experts say.", $this->smarty->fetch($tpl)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/ObjectVariableTest.php: -------------------------------------------------------------------------------- 1 | smarty->force_compile = true; 17 | } 18 | 19 | public function testObjectVariableOutput() { 20 | $object = new VariableObject; 21 | $tpl = $this->smarty->createTemplate('eval:{$object->hello}'); 22 | $tpl->assign('object', $object); 23 | $this->assertEquals('hello_world', $this->smarty->fetch($tpl)); 24 | } 25 | public function testObjectVariableOutputMethod() { 26 | $object = new VariableObject; 27 | $tpl = $this->smarty->createTemplate('eval:{$object->myhello()}'); 28 | $tpl->assign('object', $object); 29 | $this->assertEquals('hello world', $this->smarty->fetch($tpl)); 30 | } 31 | } 32 | 33 | Class VariableObject { 34 | public $hello = 'hello_world'; 35 | 36 | public function myhello() { 37 | return 'hello world'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/FetchTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('file:helloworld.tpl'); 15 | $tpl->fetch(); // compiles the template to disk 16 | $filepath = $tpl->compiled->filepath; 17 | 18 | $tpl->source->timestamp += 1; // Set source timestamp to be outdated 19 | $tpl->smarty->force_compile = 0; 20 | $tpl->smarty->compile_check = 1; 21 | $tpl->compiled->isCompiled = 0; 22 | $tpl->compiled->exists = 1; 23 | $tpl->compiled->loaded = 0; 24 | $tpl->compiled->properties = null; 25 | $tpl->properties['unifunc'] = 'doesnotexist'; 26 | 27 | $output = $tpl->fetch('helloworld.tpl'); 28 | $this->assertEquals($output, 'hello world'); // should still compile 29 | $this->assertNotEquals($tpl->compiled->filepath, $filepath); // should be a different compiled template 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructIf.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | whitespace 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
18 | 19 | 24 | 26 | 27 | 30 | 33 |
34 |         foobar
35 |     
36 |
37 |         foobar
38 |     
39 |

40 | 43 |

44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/Brainy/Runtime/Loops.php: -------------------------------------------------------------------------------- 1 | getIterator()); 21 | } elseif ($value instanceof \Iterator) { 22 | return iterator_count($value); 23 | } elseif ($value instanceof \PDOStatement) { 24 | return $value->rowCount(); 25 | } elseif ($value instanceof \Traversable) { 26 | return iterator_count($value); 27 | } elseif ($value instanceof \ArrayAccess) { 28 | if ($value->offsetExists(0)) { 29 | return 1; 30 | } 31 | } elseif (is_object($value)) { 32 | return count($value); 33 | } 34 | return 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/ParseTree.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{" hello spaced words "|strip}'); 16 | $this->assertEquals(" hello spaced words ", $this->smarty->fetch($tpl)); 17 | } 18 | 19 | public function testUnicodeSpaces() { 20 | // Some Unicode Spaces 21 | $string = " hello spaced       words "; 22 | $string = mb_convert_encoding($string, 'UTF-8', "HTML-ENTITIES"); 23 | if (iconv_strlen(preg_replace('!\s+!u', '', $string)) > 30) { 24 | $this->markTestSkipped('https://github.com/facebook/hhvm/issues/3542'); 25 | } 26 | $tpl = $this->smarty->createTemplate('eval:{"' . $string . '"|strip}'); 27 | $this->assertEquals(" hello spaced words ", $this->smarty->fetch($tpl)); 28 | } 29 | 30 | public function testLinebreak() { 31 | $tpl = $this->smarty->createTemplate('eval:{" hello 32 | spaced words "|strip}'); 33 | $this->assertEquals(" hello spaced words ", $this->smarty->fetch($tpl)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/PluginModifierCountCharactersTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"Cold Wave Linked to Temperatures."|count_characters}'); 17 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 18 | } 19 | 20 | public function testSpaces() { 21 | $result = "33"; 22 | $tpl = $this->smarty->createTemplate('eval:{"Cold Wave Linked to Temperatures."|count_characters:true}'); 23 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 24 | } 25 | 26 | public function testUmlauts() { 27 | $result = "29"; 28 | $tpl = $this->smarty->createTemplate('eval:{"Cold Wäve Linked tö Temperatures."|count_characters}'); 29 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 30 | } 31 | 32 | public function testUmlautsSpaces() { 33 | $result = "33"; 34 | $tpl = $this->smarty->createTemplate('eval:{"Cold Wäve Linked tö Temperatures."|count_characters:true}'); 35 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "box/brainy", 3 | "type": "library", 4 | "version": "4.0.0", 5 | "description": "Brainy - A smart, parallel templating engine based on Smarty", 6 | "keywords": ["templating", "smarty", "template", "mustache", "twig"], 7 | "license": "LGPL-3.0", 8 | "authors": [ 9 | { 10 | "name": "Matt Basta", 11 | "email": "basta@box.com" 12 | }, 13 | { 14 | "name": "Monte Ohrt", 15 | "email": "monte@ohrt.com" 16 | }, 17 | { 18 | "name": "Uwe Tews", 19 | "email": "uwe.tews@googlemail.com" 20 | }, 21 | { 22 | "name": "Rodney Rehm", 23 | "email": "rodney.rehm@medialize.de" 24 | } 25 | ], 26 | "require": { 27 | "php": ">=7.3" 28 | }, 29 | "require-dev": { 30 | "phpdocumentor/phpdocumentor": "3.*", 31 | "phpunit/phpunit": "9.5.21", 32 | "squizlabs/php_codesniffer": "3.7.1" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Box\\Brainy\\": "src/Brainy/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Box\\Brainy\\Tests\\": "test/" 42 | } 43 | }, 44 | "config": { 45 | "allow-plugins": { 46 | "symfony/flex": false 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/CompileForTest.php: -------------------------------------------------------------------------------- 1 | =0;$x--}{$x}{forelse}else{/for}', "9876543210"), 15 | array('{for $x=-1;$x>=0;$x--}{$x}{forelse}else{/for}', "else"), 16 | array('{for $x=0,$y=10;$x<$y;$x++}{$x}{forelse}else{/for}', "0123456789"), 17 | array('{for $x=0;$x<10;$x=$x+2}{$x}{/for}', "02468"), 18 | array('{for $x=0 to 8}{$x}{/for}', "012345678"), 19 | array('{for $x=0 to 8 step=2}{$x}{/for}', "02468"), 20 | array('{for $x=8 to 0 step=-2}{$x}{/for}', "86420"), 21 | array('{for $x=8 to 0 step=2}{$x}{forelse}step error{/for}', "step error"), 22 | array('{for $x=8 to 0 step -1 max=3}{$x}{/for}', "876"), 23 | ); 24 | } 25 | 26 | /** 27 | * @dataProvider dataProviderForForLoops 28 | */ 29 | public function testForLoops($template, $expected) 30 | { 31 | $this->assertEquals($expected, $this->smarty->fetch('eval:' . $template)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: parsers autoload test 2 | 3 | autoload: 4 | composer dump-autoload 5 | 6 | parsers: src/Brainy/Compiler/Lexer.php src/Brainy/Compiler/Parser.php clean 7 | 8 | src/Brainy/Compiler/Lexer.php: build/parsers/Lexer.plex 9 | cd build/parsers/ && php Create_Template_Parser.php 10 | mv build/parsers/Lexer.php src/Brainy/Compiler/Lexer.php 11 | 12 | src/Brainy/Compiler/Parser.php: build/parsers/Parser.y 13 | cd build/parsers/ && php Create_Template_Parser.php 14 | mv build/parsers/Parser.php src/Brainy/Compiler/Parser.php 15 | 16 | 17 | test: clean 18 | mkdir -p test/compiled 19 | ./vendor/bin/phpunit 20 | 21 | test-hhvm: clean 22 | mkdir -p test/compiled 23 | ./phpunit-hhvm 24 | 25 | coverage: clean 26 | mkdir -p coverage 27 | ./vendor/bin/phpunit --coverage-html ./coverage 28 | 29 | lint: 30 | vendor/bin/phpcs -v src/ 31 | 32 | clean: 33 | rm -rf test/compiled/* 34 | rm -f build/parsers/*.out 35 | rm -f build/parsers/Lexer.php build/parsers/Parser.php 36 | 37 | docs: clean 38 | vendor/bin/phpdoc -d src/Brainy -t docs/ --ignore "*_internal_compile_*,*_internal_*parser.php,*_internal_*lexer.php,*/plugins/*" 39 | 40 | docs-branch: coverage docs 41 | git branch -D gh-pages 42 | git checkout -b gh-pages 43 | git rm -rf build src test 44 | mv docs/* . 45 | git add . 46 | git add -f coverage 47 | git commit -a -u -m "Docs update" 48 | git checkout master 49 | git clean -f -d 50 | echo "Run git push -f origin gh-pages" 51 | -------------------------------------------------------------------------------- /test/ResourcePluginTest.php: -------------------------------------------------------------------------------- 1 | smarty->registerResource('db2', new ResourcePlugins\ResourceDB2()); 16 | $this->assertEquals('hello world', $this->smarty->fetch('db2:test')); 17 | } 18 | /** 19 | * test resource plugin non-existent compiled cache of a recompiling resource 20 | */ 21 | public function testResourcePluginRecompiledCompiledFilepath() { 22 | $this->smarty->registerResource('db2', new ResourcePlugins\ResourceDB2()); 23 | $tpl = $this->smarty->createTemplate('db2:test.tpl'); 24 | $expected = realpath('test/compiled/'.sha1('db2:test.tpl').'.db2.test.tpl.php'); 25 | $this->assertFalse((bool) $expected); 26 | $this->assertFalse($tpl->compiled->filepath); 27 | } 28 | /** 29 | * test resource plugin timesatmp 30 | */ 31 | public function testResourcePluginTimestamp() { 32 | $this->smarty->registerResource('db2', new ResourcePlugins\ResourceDB2()); 33 | $tpl = $this->smarty->createTemplate('db2:test'); 34 | $this->assertTrue(is_integer($tpl->source->timestamp)); 35 | $this->assertEquals(10, strlen($tpl->source->timestamp)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/CompileFunctionPluginTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('functionplugintest.tpl', null, $this->smarty); 16 | $this->assertEquals("10", $this->smarty->fetch($tpl)); 17 | } 18 | public function testFunctionPluginFromCompiledTemplateFile() { 19 | $this->smarty->force_compile = false; 20 | $tpl = $this->smarty->createTemplate('functionplugintest.tpl', null, $this->smarty); 21 | $this->assertEquals("10", $this->smarty->fetch($tpl)); 22 | } 23 | public function testFunctionPluginRegisteredFunction() { 24 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_FUNCTION, 'plugintest', '\Box\Brainy\Tests\CompileFunctionPluginTest::myplugintest'); 25 | $tpl = $this->smarty->createTemplate('eval:{plugintest foo=bar}', null, $this->smarty); 26 | $this->assertEquals("plugin test called bar", $this->smarty->fetch($tpl)); 27 | } 28 | 29 | public function testMultiLineTags() { 30 | $this->assertEquals("10", $this->smarty->fetch("eval:{counter\n\tstart=10}")); 31 | } 32 | 33 | 34 | public static function myplugintest($params, &$smarty) 35 | { 36 | return "plugin test called $params[foo]"; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /test/GenericErrorTest.php: -------------------------------------------------------------------------------- 1 | expectException(\Box\Brainy\Exceptions\SmartyException::class); 11 | $this->expectExceptionMessage('Unable to load template'); 12 | $this->smarty->display('eval:{include file=\'no.tpl\'}'); 13 | } 14 | 15 | public function testUnknownTagError() 16 | { 17 | $this->expectException(\Box\Brainy\Exceptions\SmartyException::class); 18 | $this->expectExceptionMessage('Call to undefined function \'unknown\'. Defined functions: '); 19 | $this->smarty->display('eval:{unknown}'); 20 | } 21 | 22 | public function testUnclosedTagError() 23 | { 24 | $this->expectException(\Box\Brainy\Exceptions\SmartyCompilerException::class); 25 | $this->expectExceptionMessage('"{if true}" unclosed {if} tag'); 26 | $this->smarty->display('eval:{if true}'); 27 | } 28 | public function testSyntaxError() 29 | { 30 | $this->expectException(\Box\Brainy\Exceptions\SmartyCompilerException::class); 31 | $this->expectExceptionMessage('"{assign var=}" - Unexpected "}"'); 32 | $this->smarty->display('eval:{assign var=}'); 33 | } 34 | 35 | public function testEmptyTemplate() 36 | { 37 | $tpl = $this->smarty->createTemplate('eval:'); 38 | $this->assertEmpty($this->smarty->display($tpl)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /test/CompileCompilerPluginTest.php: -------------------------------------------------------------------------------- 1 | smarty->force_compile = true; 17 | } 18 | 19 | /** 20 | * test compiler plugin tag in template file 21 | */ 22 | public function testCompilerPluginFromTemplateFile() { 23 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER,'compilerplugin', '\Box\Brainy\Tests\CompileCompilerPluginTest::mycompilerplugin'); 24 | $tpl = $this->smarty->createTemplate('compilerplugintest.tpl'); 25 | $this->assertEquals("Hello World", trim($this->smarty->fetch($tpl))); 26 | } 27 | /** 28 | * test compiler plugin tag in compiled template file 29 | */ 30 | public function testCompilerPluginFromCompiledTemplateFile() { 31 | $this->smarty->force_compile = false; 32 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER, 'compilerplugin', '\Box\Brainy\Tests\CompileCompilerPluginTest::mycompilerplugin'); 33 | $tpl = $this->smarty->createTemplate('compilerplugintest.tpl'); 34 | $this->assertEquals("Hello World", trim($this->smarty->fetch($tpl))); 35 | } 36 | 37 | 38 | public static function mycompilerplugin($params, $compiler) { 39 | return 'echo \'Hello World\';'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/CompileStripTest.php: -------------------------------------------------------------------------------- 1 | \n ", '
'), 17 | array("\n foo\n
", 'foo
'), 18 | array("", '
'), 19 | array("", ''), 20 | array("foo ", 'foo '), 21 | array("foo> ", 'foo>'), 22 | array("foo   bar", 'foo   bar'), 23 | array("Hey there", 'Hey there'), 24 | array("
\n {* foo *}\n {'bar'}", '
bar'), 25 | ); 26 | } 27 | 28 | /** 29 | * @dataProvider dataProviderForStripTests 30 | */ 31 | public function testStrip($source, $output) { 32 | $tpl = $this->smarty->createTemplate('eval:{strip}' . $source); 33 | $this->assertEquals($output, $this->smarty->fetch($tpl)); 34 | } 35 | 36 | public function testUnbalancedStrip() { 37 | $this->expectException(\Box\Brainy\Exceptions\SmartyCompilerException::class); 38 | $this->expectExceptionMessage('Unbalanced {strip} tags'); 39 | $this->smarty->display("eval:{strip}
\n
{/strip}{/strip}"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/AssignGlobalTest.php: -------------------------------------------------------------------------------- 1 | smarty->assignGlobal('foo', 'bar'); 16 | $this->assertEquals('bar', $this->smarty->getGlobal('foo')); 17 | } 18 | 19 | public function testAssignGlobalGetGlobalArray() { 20 | $this->smarty->assignGlobal('foo', array('foo' => 'bar', 'foo2' => 'bar2')); 21 | $a1 = array('foo' => array('foo' => 'bar', 'foo2' => 'bar2')); 22 | $a2 = $this->smarty->getGlobal(); 23 | $this->assertTrue($a1 === $a2); 24 | } 25 | 26 | public function testAssignGlobalTag() { 27 | $this->smarty->assignGlobal('foo', 'bar'); 28 | $this->assertEquals('bar', $this->smarty->fetch('eval:{$foo}')); 29 | $this->assertEquals('buh', $this->smarty->fetch('eval:{assign var=foo value=buh scope=global}{$foo}')); 30 | $this->assertEquals('buh', $this->smarty->fetch('eval:{$foo}')); 31 | $this->assertEquals('buh', $this->smarty->getGlobal('foo')); 32 | } 33 | 34 | public function testGlobalVarArrayTag() { 35 | $this->smarty->assignGlobal('foo', array('foo' => 'bar', 'foo2' => 'bar2')); 36 | $this->assertEquals('bar2', $this->smarty->fetch('eval:{$foo.foo2}')); 37 | $this->assertEquals('bar', $this->smarty->fetch('eval:{$foo.foo}')); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Brainy/plugins/shared.make_timestamp.php: -------------------------------------------------------------------------------- 1 | 11 | * Purpose: used by other smarty functions to make a timestamp from a string. 12 | * 13 | * @author Monte Ohrt 14 | * @param DateTime|int|string $string date object, timestamp or string that can be converted using strtotime() 15 | * @return int 16 | */ 17 | function smarty_make_timestamp($string) 18 | { 19 | if (empty($string)) { 20 | // use "now": 21 | return time(); 22 | } elseif ($string instanceof DateTime) { 23 | return $string->getTimestamp(); 24 | } elseif (strlen($string) == 14 && ctype_digit($string)) { 25 | // it is mysql timestamp format of YYYYMMDDHHMMSS? 26 | return mktime( 27 | substr($string, 8, 2), 28 | substr($string, 10, 2), 29 | substr($string, 12, 2), 30 | substr($string, 4, 2), 31 | substr($string, 6, 2), 32 | substr($string, 0, 4) 33 | ); 34 | } elseif (is_numeric($string)) { 35 | // it is a numeric string, we handle it as timestamp 36 | return (int) $string; 37 | } else { 38 | // strtotime should handle it 39 | $time = strtotime($string); 40 | if ($time == -1 || $time === false) { 41 | // strtotime() was not able to parse $string, use "now": 42 | return time(); 43 | } 44 | 45 | return $time; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Brainy/Templates/CompiledTemplate.php: -------------------------------------------------------------------------------- 1 | source = $source; 63 | } 64 | 65 | /** 66 | * Loads the template from disk 67 | * @param Template $_smarty_tpl 68 | * @return void 69 | */ 70 | public function load($_smarty_tpl) 71 | { 72 | include $this->filepath; 73 | $this->loaded = true; 74 | $this->isCompiled = true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructCall.php: -------------------------------------------------------------------------------- 1 | getUniqueVarName(); 22 | // Evaluate the function name dynamically at runtime 23 | $output = "$tmpVar = $name;\n"; 24 | // Safety Dance 25 | $output .= "if (!array_key_exists($tmpVar, \$_smarty_tpl->tpl_vars['smarty']['functions'])) {\n"; 26 | $output .= " \$funcs = implode(', ', array_keys(\$_smarty_tpl->tpl_vars['smarty']['functions'])) ?: '';\n"; 27 | $output .= " throw new \\Box\\Brainy\\Exceptions\\SmartyException('Call to undefined function \\'' . $tmpVar . '\\'. Defined functions: ' . \$funcs);\n"; 28 | $output .= "}\n"; 29 | 30 | if ($assign) { 31 | $output .= "ob_start();\n"; 32 | } 33 | $output .= "\$_smarty_tpl->tpl_vars['smarty']['functions'][$tmpVar]($paramArray);\n"; 34 | if ($assign) { 35 | $output .= "\$_smarty_tpl->setVariable($assign, ob_get_clean());\n"; 36 | } 37 | 38 | return $output; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.truncate.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: truncate
14 | * Purpose: Truncate a string to a certain length if necessary, 15 | * optionally splitting in the middle of a word, and 16 | * appending the $etc string or inserting $etc into the middle. 17 | * 18 | * @link http://smarty.php.net/manual/en/language.modifier.truncate.php truncate (Smarty online manual) 19 | * @author Monte Ohrt 20 | * @param string $string input string 21 | * @param integer $length length of truncated text 22 | * @param string $etc end string 23 | * @param boolean $break_words truncate at word boundary 24 | * @param boolean $middle truncate in the middle of text 25 | * @return string truncated string 26 | */ 27 | function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_words = false, $middle = false) 28 | { 29 | if ($length == 0) { 30 | return ''; 31 | } 32 | 33 | if (mb_strlen($string, 'UTF-8') > $length) { 34 | $length -= min($length, mb_strlen($etc, 'UTF-8')); 35 | if (!$break_words && !$middle) { 36 | $string = preg_replace('/\s+?(\S+)?$/u', '', mb_substr($string, 0, $length + 1, 'UTF-8')); 37 | } 38 | if (!$middle) { 39 | return mb_substr($string, 0, $length, 'UTF-8') . $etc; 40 | } 41 | 42 | return mb_substr($string, 0, $length / 2, 'UTF-8') . $etc . mb_substr($string, - $length / 2, $length, 'UTF-8'); 43 | } 44 | 45 | return $string; 46 | } 47 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/TemplateBuffer.php: -------------------------------------------------------------------------------- 1 | subtrees[] = $subtree; 22 | } 23 | 24 | /** 25 | * @return string 26 | */ 27 | public function toInlineData() 28 | { 29 | throw new \Box\Brainy\Exceptions\SmartyException('Template buffer cast to inline template data'); 30 | } 31 | 32 | /** 33 | * Sanitize and merge subtree buffers together 34 | * 35 | * @return string template code content 36 | */ 37 | public function toSmartyPHP() 38 | { 39 | $code = ''; 40 | $buffer = ''; 41 | foreach ($this->subtrees as $node) { 42 | if ($node->canCombineInlineData()) { 43 | $buffer .= $node->toInlineData(); 44 | continue; 45 | } 46 | 47 | if ($buffer !== '') { 48 | $code .= $this->echoData(var_export($buffer, true)); 49 | $buffer = ''; 50 | } 51 | 52 | $code .= $node->toSmartyPHP(); 53 | } 54 | 55 | if ($buffer !== '') { 56 | $code .= $this->echoData(var_export($buffer, true)); 57 | } 58 | 59 | return $code; 60 | } 61 | 62 | /** 63 | * @return bool 64 | */ 65 | public function canCombineInlineData() 66 | { 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.replace.php: -------------------------------------------------------------------------------- 1 | 12 | * Name: replace
13 | * Purpose: simple search/replace 14 | * 15 | * @link http://smarty.php.net/manual/en/language.modifier.replace.php replace (Smarty online manual) 16 | * @author Monte Ohrt 17 | * @author Uwe Tews 18 | * @param string $string input string 19 | * @param string $search text to search for 20 | * @param string $replace replacement text 21 | * @return string 22 | */ 23 | function smarty_modifier_replace($subject, $search, $replace) 24 | { 25 | if (!is_array($search) && is_array($replace)) { 26 | return false; 27 | } 28 | if (is_array($subject)) { 29 | // call mb_replace for each single string in $subject 30 | foreach ($subject as &$string) { 31 | $string = &smarty_modifier_replace($string, $search, $replace); 32 | } 33 | } elseif (is_array($search)) { 34 | if (!is_array($replace)) { 35 | foreach ($search as &$string) { 36 | $subject = smarty_modifier_replace($subject, $string, $replace); 37 | } 38 | } else { 39 | $n = max(count($search), count($replace)); 40 | while ($n--) { 41 | $subject = smarty_modifier_replace($subject, current($search), current($replace)); 42 | next($search); 43 | next($replace); 44 | } 45 | } 46 | } else { 47 | $parts = mb_split(preg_quote($search), $subject); 48 | $subject = implode($replace, $parts); 49 | } 50 | 51 | return $subject; 52 | } 53 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.regex_replace.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: regex_replace
14 | * Purpose: regular expression search/replace 15 | * 16 | * @link http://smarty.php.net/manual/en/language.modifier.regex.replace.php 17 | * regex_replace (Smarty online manual) 18 | * @author Monte Ohrt 19 | * @param string $string input string 20 | * @param string|array $search regular expression(s) to search for 21 | * @param string|array $replace string(s) that should be replaced 22 | * @return string 23 | */ 24 | function smarty_modifier_regex_replace($string, $search, $replace) 25 | { 26 | if (is_array($search)) { 27 | foreach ($search as $idx => $s) { 28 | $search[$idx] = _smarty_regex_replace_check($s); 29 | } 30 | } else { 31 | $search = _smarty_regex_replace_check($search); 32 | } 33 | 34 | return preg_replace($search, $replace, $string); 35 | } 36 | 37 | /** 38 | * @param string $search string(s) that should be replaced 39 | * @return string 40 | * @ignore 41 | */ 42 | function _smarty_regex_replace_check($search) 43 | { 44 | // null-byte injection detection 45 | // anything behind the first null-byte is ignored 46 | if (($pos = strpos($search, "\0")) !== false) { 47 | $search = substr($search, 0, $pos); 48 | } 49 | // remove eval-modifier from $search 50 | if (preg_match('!([a-zA-Z\s]+)$!s', $search, $match) && (strpos($match[1], 'e') !== false)) { 51 | $search = substr($search, 0, -strlen($match[1])) . preg_replace('![e\s]+!', '', $match[1]); 52 | } 53 | 54 | return $search; 55 | } 56 | -------------------------------------------------------------------------------- /test/PluginModifierCountSentencesTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"hello world."|count_sentences}'); 16 | $this->assertEquals("1", $this->smarty->fetch($tpl)); 17 | $tpl = $this->smarty->createTemplate('eval:{"hello world. I\'m another? Sentence!"|count_sentences}'); 18 | $this->assertEquals("3", $this->smarty->fetch($tpl)); 19 | $tpl = $this->smarty->createTemplate('eval:{"hello world.wrong"|count_sentences}'); 20 | $this->assertEquals("0", $this->smarty->fetch($tpl)); 21 | } 22 | 23 | public function testUmlauts() { 24 | if (preg_match_all('/\w/Su', 'ä') != 1) { 25 | $this->markTestSkipped('https://github.com/facebook/hhvm/issues/3543'); 26 | } 27 | $tpl = $this->smarty->createTemplate('eval:{"hello worldä."|count_sentences}'); 28 | $this->assertEquals("1", $this->smarty->fetch($tpl)); 29 | $tpl = $this->smarty->createTemplate('eval:{"hello worldü. ä\'m another? Sentence!"|count_sentences}'); 30 | $this->assertEquals("3", $this->smarty->fetch($tpl)); 31 | $tpl = $this->smarty->createTemplate('eval:{"hello worlä.ärong"|count_sentences}'); 32 | $this->assertEquals("0", $this->smarty->fetch($tpl)); 33 | $tpl = $this->smarty->createTemplate('eval:{"hello worlä.wrong"|count_sentences}'); 34 | $this->assertEquals("0", $this->smarty->fetch($tpl)); 35 | $tpl = $this->smarty->createTemplate('eval:{"hello world.ärong"|count_sentences}'); 36 | $this->assertEquals("0", $this->smarty->fetch($tpl)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Brainy/Runtime/PluginLoader.php: -------------------------------------------------------------------------------- 1 | getPluginsDir(); 24 | foreach ($pluginDirs as $dir) { 25 | $ffile = $dir . $filename; 26 | if (!file_exists($ffile)) { 27 | continue; 28 | } 29 | include $ffile; 30 | return true; 31 | } 32 | 33 | return false; 34 | } 35 | 36 | /** 37 | * Return the filename of the plugin given its name and type 38 | * @param string $type The type of the plugin 39 | * @param string $name The name of the plugin 40 | * @return string The filename of the plugin 41 | */ 42 | public static function getPluginFilename($type, $name) 43 | { 44 | return $type . '.' . $name . '.php'; 45 | } 46 | 47 | /** 48 | * Return the name of a plugin function given its name and type 49 | * @param string $type The type of the plugin 50 | * @param string $name The name of the plugin 51 | * @return string The name of the plugin's function 52 | */ 53 | public static function getPluginFunction($type, $name) 54 | { 55 | return 'smarty_' . $type . '_' . $name; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/LiteralTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate("eval:{literal} {\$foo} {/literal}"); 14 | $this->assertEquals(' {$foo} ', $this->smarty->fetch($tpl)); 15 | } 16 | 17 | public function testLiteralTagWithDoubleBackslashes() { 18 | $tpl = $this->smarty->createTemplate("eval:{literal} \\\\ {/literal}"); // \\\\ -> \\ 19 | $this->assertEquals(' \\\\ ', $this->smarty->fetch($tpl)); 20 | } 21 | 22 | public function testBlockInLiteralTag() { 23 | $tpl = $this->smarty->createTemplate("eval:{literal} {block \"foo\"}{\$foo}{/block} {/literal}"); 24 | $this->assertEquals(' {block "foo"}{$foo}{/block} ', $this->smarty->fetch($tpl)); 25 | } 26 | 27 | /* 28 | * Test auto literal space 29 | */ 30 | public function testAutoLiteralSpace() { 31 | $tpl = $this->smarty->createTemplate("eval: { \$foo} "); 32 | $tpl->assign('foo', 'literal'); 33 | $this->assertEquals(' { $foo} ', $this->smarty->fetch($tpl)); 34 | } 35 | 36 | /* 37 | * Test auto literal line break 38 | */ 39 | public function testAutoLiteralLineBreak() { 40 | $tpl = $this->smarty->createTemplate("eval: {\n\$foo} "); 41 | $tpl->assign('foo', 'literal'); 42 | $this->assertEquals(" {\n\$foo} ", $this->smarty->fetch($tpl)); 43 | } 44 | 45 | /* 46 | * Test auto literal disabled 47 | */ 48 | public function testAutoLiteralDisabled() { 49 | $this->smarty->auto_literal = false; 50 | $tpl = $this->smarty->createTemplate("eval: { \$foo} "); 51 | $tpl->assign('foo', 'literal'); 52 | $this->assertEquals(' literal ', $this->smarty->fetch($tpl)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructCapture.php: -------------------------------------------------------------------------------- 1 | assertIsNotStrict('Capture shorthand is not allowed in strict mode. Use the name="" attribute instead.'); 18 | $name = $args[0]; 19 | } 20 | 21 | if (!$name && !$assign) { 22 | $name = "'default'"; 23 | } 24 | 25 | if ($name && $assign) { 26 | throw new \Box\Brainy\Exceptions\SmartyCompilerException('{capture} tags may not set both `name` and `assign`'); 27 | } 28 | 29 | self::openTag($compiler, 'capture', array($name, $assign)); 30 | return "ob_start();\n"; 31 | } 32 | 33 | /** 34 | * @param \Box\Brainy\Compiler\TemplateCompiler $compiler A compiler reference 35 | * @param array|null $args Arguments 36 | * @return mixed 37 | */ 38 | public static function compileClose(\Box\Brainy\Compiler\TemplateCompiler $compiler, $args) 39 | { 40 | list($name, $assign) = self::closeTag($compiler, array('capture')); 41 | 42 | if (isset($assign)) { 43 | return '$_smarty_tpl->setVariable(' . $assign . ', ob_get_clean());'; 44 | } elseif (isset($name)) { 45 | return '$_smarty_tpl->tpl_vars[\'smarty\'][\'capture\'][' . $name . '] = ob_get_clean();'; 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/ArrayTest.php: -------------------------------------------------------------------------------- 1 | 1,"b"=>2,"c"=>3,$x=>4]}{$foo["a"]}{$foo["b"]}{$foo["c"]}{$foo["d"]}'), 17 | array('b', '{$foo=[1,2,[a,b,c],4,5]}{$foo[2][1]}'), 18 | 19 | // Parse tests to exercise the grammar 20 | array('', '{$foo=[1,2,[7,8,9],4,5]}'), 21 | 22 | // Lookup tests 23 | array('1', '{$foo=[1,2,[7,8,9],4,5]}{$foo[0]}'), 24 | array('8', '{$foo=[1,2,[7,8,9],4,5]}{$foo[2][1]}'), 25 | 26 | array('9', '{$foo=[1,2,[7,8,9],4,5]}{$foo[2][1]+1}'), 27 | 28 | array('9', '{$foo=[1,2,[7,8,9],4,5]}{$foo.2.1+1}'), 29 | array('10', '{$foo=[1,2,[7,8,9],4,5]}{2+$foo[2][1]}'), 30 | array('10', '{$foo=[1,2,[7,8,9],4,5]}{2+$foo.2.1}'), 31 | array('15', '{$foo=[1,2,[7,8,9],4,5]}{$foo[2][0]+$foo[2][1]}'), 32 | array('15', '{$foo=[1,2,[7,8,9],4,5]}{$foo.2.0+$foo.2.1}'), 33 | array('7', '{$foo=[1,2,[7,8,9],4,5]}{$x=2}{$y=0}{$foo.$x.$y}'), 34 | array('7', '{$foo=[1,2,[7,8,9],4,5]}{$x=2}{$foo.$x.0}'), 35 | array('7', '{$foo=[1,2,[7,8,9],4,5]}{$x=0}{$foo.2.$x}'), 36 | array('7', '{$foo=[1,2,[7,8,9],4,5]}{$x=[1,0]}{$foo.2.{$x.1}}'), 37 | ); 38 | } 39 | 40 | /** 41 | * @dataProvider dataProviderForArrays 42 | */ 43 | public function testArrays($expected, $code) 44 | { 45 | $this->assertEquals($expected, $this->smarty->fetch('eval:' . $code)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/RegisterModifierTest.php: -------------------------------------------------------------------------------- 1 | modifier / unregister->modifier methods 4 | * 5 | * @package PHPunit 6 | * @author Uwe Tews 7 | */ 8 | 9 | namespace Box\Brainy\Tests; 10 | 11 | 12 | class RegisterModifierTest extends Smarty_TestCase 13 | { 14 | 15 | public static function mymodifier($a, $b, $c) 16 | { 17 | return "$a function $b $c"; 18 | } 19 | 20 | /** 21 | * test register->modifier method for function 22 | */ 23 | public function testRegisterModifier() { 24 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_MODIFIER, 'testmodifier', '\Box\Brainy\Tests\RegisterModifierTest::mymodifier'); 25 | $this->assertEquals('\Box\Brainy\Tests\RegisterModifierTest::mymodifier', $this->smarty->registered_plugins[\Box\Brainy\Brainy::PLUGIN_MODIFIER]['testmodifier']); 26 | $this->smarty->assign('foo', 'foo'); 27 | $this->smarty->assign('bar', 'bar'); 28 | $this->assertEquals('foo function blar bar', $this->smarty->fetch('eval:{$foo|testmodifier:blar:$bar}')); 29 | } 30 | /** 31 | * test unregister->modifier method 32 | */ 33 | public function testUnregisterModifier() { 34 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_MODIFIER,'testmodifier', '\Box\Brainy\Tests\RegisterModifierTest::mymodifier'); 35 | $this->smarty->unregisterPlugin(\Box\Brainy\Brainy::PLUGIN_MODIFIER,'testmodifier'); 36 | $this->assertFalse(isset($this->smarty->registered_plugins[\Box\Brainy\Brainy::PLUGIN_MODIFIER]['testmodifier'])); 37 | } 38 | /** 39 | * test unregister->modifier method not registered 40 | */ 41 | public function testUnregisterModifierNotRegistered() { 42 | $this->smarty->unregisterPlugin(\Box\Brainy\Brainy::PLUGIN_MODIFIER,'testmodifier'); 43 | $this->assertFalse(isset($this->smarty->registered_plugins[\Box\Brainy\Brainy::PLUGIN_MODIFIER]['testmodifier'])); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/ClearAssignTest.php: -------------------------------------------------------------------------------- 1 | smarty->safe_lookups = \Box\Brainy\Brainy::LOOKUP_SAFE; 17 | $this->smartyBC->safe_lookups = \Box\Brainy\Brainy::LOOKUP_SAFE; 18 | 19 | $this->smarty->assign('foo','foo'); 20 | $this->smarty->assign('bar','bar'); 21 | $this->smarty->assign('blar','blar'); 22 | 23 | $this->smartyBC->assign('foo','foo'); 24 | $this->smartyBC->assign('bar','bar'); 25 | $this->smartyBC->assign('blar','blar'); 26 | } 27 | 28 | /** 29 | * test all variables accessable 30 | */ 31 | public function testAllVariablesAccessable() { 32 | $this->assertEquals('foobarblar', $this->smarty->fetch('eval:{$foo}{$bar}{$blar}')); 33 | } 34 | 35 | /** 36 | * test simple clear assign 37 | */ 38 | public function testClearAssign() { 39 | $this->smarty->clearAssign('blar'); 40 | $this->assertEquals('foobar', $this->smarty->fetch('eval:{$foo}{$bar}{$blar}')); 41 | } 42 | public function testSmarty2ClearAssign() { 43 | $this->smartyBC->clear_assign('blar'); 44 | $this->assertEquals('foobar', $this->smartyBC->fetch('eval:{$foo}{$bar}{$blar}')); 45 | } 46 | /** 47 | * test clear assign array of variables 48 | */ 49 | public function testArrayClearAssign() { 50 | $this->smarty->clearAssign(array('blar','foo')); 51 | $this->assertEquals('bar', $this->smarty->fetch('eval:{$foo}{$bar}{$blar}')); 52 | } 53 | public function testSmarty2ArrayClearAssign() { 54 | $this->smartyBC->clear_assign(array('blar','foo')); 55 | $this->assertEquals('bar', $this->smartyBC->fetch('eval:{$foo}{$bar}{$blar}')); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Helpers/DoubleQuoted.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 17 | } 18 | 19 | /** 20 | * @param ParseTree $subtree parsetree buffer 21 | */ 22 | public function appendSubtree(ParseTree $subtree) 23 | { 24 | if ($subtree instanceof Tag) { 25 | throw new \Box\Brainy\Exceptions\SmartyCompilerException('Cannot use tags inside double quoted strings'); 26 | } 27 | $this->subtrees[] = $subtree; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function toInlineData() 34 | { 35 | return $this->toSmartyPHP(); 36 | } 37 | 38 | /** 39 | * @return string compiled template code 40 | */ 41 | public function toSmartyPHP() 42 | { 43 | $code = ''; 44 | $buffer = ''; 45 | foreach ($this->subtrees as $subtree) { 46 | if ($subtree->canCombineInlineData()) { 47 | $buffer .= $subtree->toInlineData(); 48 | continue; 49 | } 50 | 51 | if ($code) { 52 | $code .= '.'; 53 | } 54 | 55 | if ($buffer) { 56 | $code .= $this->escapeData($buffer) . '.'; 57 | $buffer = ''; 58 | } 59 | 60 | $code .= $subtree->toSmartyPHP(); 61 | 62 | } 63 | 64 | if ($buffer) { 65 | if ($code) { 66 | $code .= '.'; 67 | } 68 | $code .= $this->escapeData($buffer); 69 | } 70 | 71 | return $code; 72 | } 73 | 74 | /** 75 | * @return bool 76 | */ 77 | public function canCombineInlineData() 78 | { 79 | return false; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/FunctionTest.php: -------------------------------------------------------------------------------- 1 | smarty->enableSecurity(); 18 | } 19 | 20 | public function testUnknownFunction() 21 | { 22 | $this->expectException(\Box\Brainy\Exceptions\SmartyCompilerException::class); 23 | $this->expectExceptionMessage("PHP function 'unknown' not allowed by security setting"); 24 | $this->smarty->display('eval:{unknown()}'); 25 | } 26 | 27 | public function testTemplateFunctionDefaultParam() { 28 | $this->assertEquals( 29 | $this->smarty->fetch('eval:{function name="foo" def="hello"}{$def}{/function}{foo}'), 30 | 'hello' 31 | ); 32 | } 33 | 34 | public function testTemplateFunctionDefaultParamOverridden() { 35 | $this->assertEquals( 36 | $this->smarty->fetch('string:{function name="bar" def="hello"}{$def}{/function}{bar def="goodbye"}'), 37 | 'goodbye' 38 | ); 39 | } 40 | 41 | public function testTemplateFunctionParam() { 42 | $this->assertEquals( 43 | $this->smarty->fetch('eval:{function name="zip"}{$var}{/function}{zip var="this is not a default param"}'), 44 | 'this is not a default param' 45 | ); 46 | } 47 | 48 | public function testTemplateFunctionWithPlugin() { 49 | $this->assertEquals( 50 | $this->smarty->fetch('eval:{function name="zap"}{$var|escape}{/function}{zap var="foo&bar"}'), 51 | 'foo&bar' 52 | ); 53 | } 54 | 55 | public function testTemplateFunctionWithTrailingLineBreak() { 56 | $this->assertEquals( 57 | $this->smarty->fetch("eval:{function name=\"hello\"}{\$var}{/function}{hello var=\"notrailingwhitespace\"}<"), 58 | 'notrailingwhitespace<' 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Brainy/plugins/function.counter.php: -------------------------------------------------------------------------------- 1 | 12 | * Name: counter
13 | * Purpose: print out a counter value 14 | * 15 | * @author Monte Ohrt 16 | * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} 17 | * (Smarty online manual) 18 | * @param array $params parameters 19 | * @param Template $template template object 20 | * @return string|null 21 | */ 22 | function smarty_function_counter($params, $template) 23 | { 24 | static $counters = array(); 25 | 26 | $name = (isset($params['name'])) ? $params['name'] : 'default'; 27 | if (!isset($counters[$name])) { 28 | $counters[$name] = array( 29 | 'start'=>1, 30 | 'skip'=>1, 31 | 'direction'=>'up', 32 | 'count'=>1, 33 | ); 34 | } 35 | $counter =& $counters[$name]; 36 | 37 | if (isset($params['start'])) { 38 | $counter['start'] = $counter['count'] = (int) $params['start']; 39 | } 40 | 41 | if (!empty($params['assign'])) { 42 | $counter['assign'] = $params['assign']; 43 | } 44 | 45 | if (isset($counter['assign'])) { 46 | $template->assign($counter['assign'], $counter['count']); 47 | } 48 | 49 | $print = isset($params['print']) ? (bool) $params['print'] : empty($counter['assign']); 50 | 51 | if ($print) { 52 | $retval = $counter['count']; 53 | } else { 54 | $retval = null; 55 | } 56 | 57 | if (isset($params['skip'])) { 58 | $counter['skip'] = $params['skip']; 59 | } 60 | 61 | if (isset($params['direction'])) { 62 | $counter['direction'] = $params['direction']; 63 | } 64 | 65 | if ($counter['direction'] == "down") { 66 | $counter['count'] -= $counter['skip']; 67 | } else { 68 | $counter['count'] += $counter['skip']; 69 | } 70 | 71 | return (string) $retval; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/Brainy/Resources/ResourceEval.php: -------------------------------------------------------------------------------- 1 | uid = $source->filepath = sha1($source->name); 28 | $source->timestamp = false; 29 | $source->exists = true; 30 | } 31 | 32 | /** 33 | * Load template's source from $resource_name into current template object 34 | * 35 | * @uses decode() to decode base64 and urlencoded template_resources 36 | * @param TemplateSource $source source object 37 | * @return string template source 38 | */ 39 | public function getContent(TemplateSource $source) 40 | { 41 | return $source->name; 42 | } 43 | 44 | /** 45 | * modify resource_name according to resource handlers specifications 46 | * 47 | * @param \Box\Brainy\Brainy $brainy 48 | * @param string $resource_name resource_name to make unique 49 | * @return string unique resource name 50 | */ 51 | protected function buildUniqueResourceName(\Box\Brainy\Brainy $brainy, $resource_name) 52 | { 53 | return get_class($this) . '#' . $resource_name; 54 | } 55 | 56 | /** 57 | * Determine basename for compiled filename 58 | * 59 | * @param TemplateSource $source source object 60 | * @return string resource's basename 61 | */ 62 | protected function getBasename(TemplateSource $source) 63 | { 64 | return ''; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructBlockTerminal.php: -------------------------------------------------------------------------------- 1 | getUniqueVarName(); 18 | 19 | self::openTag( 20 | $compiler, 21 | 'block', 22 | array( 23 | 'name' => $name, 24 | 'childVar' => $childBlockVar, 25 | 'forced' => $forced, 26 | ) 27 | ); 28 | 29 | $nameVar = '$' . $compiler->getUniqueVarName(); 30 | $output = "$nameVar = $name;\n"; // Guaranteed once execution 31 | 32 | if ($forced) { 33 | $output .= "if (array_key_exists($nameVar, \$_smarty_tpl->tpl_vars['smarty']['blocks'])) {\n"; 34 | $output .= " $childBlockVar = \$_smarty_tpl->tpl_vars['smarty']['blocks'][$nameVar];\n"; 35 | $output .= "} else {\n"; 36 | $output .= " $childBlockVar = null;\n"; 37 | $output .= "}\n"; 38 | return $output; 39 | } 40 | 41 | 42 | $output .= "if (array_key_exists($nameVar, \$_smarty_tpl->tpl_vars['smarty']['blocks'])) {\n"; 43 | $output .= " \$_smarty_tpl->tpl_vars['smarty']['blocks'][$nameVar](\$_smarty_tpl);\n"; 44 | $output .= "} else {\n"; 45 | $output .= " $childBlockVar = null;\n"; 46 | return $output; 47 | } 48 | 49 | public static function compileClose(\Box\Brainy\Compiler\TemplateCompiler $compiler, $args) 50 | { 51 | $data = self::closeTag($compiler, 'block'); 52 | if ($data['forced']) { 53 | return ''; 54 | } 55 | return "}\n"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/PluginModifierEscapeTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('https://github.com/facebook/hhvm/issues/2266'); 18 | } 19 | } 20 | 21 | public function testHtml() { 22 | $tpl = $this->smarty->createTemplate('eval:{"I\'m some to ä be \"escaped\" or ©"|escape:"html"}'); 23 | $this->assertEquals("I'm some <html> to ä be "escaped" or &copy;", $this->smarty->fetch($tpl)); 24 | } 25 | 26 | public function testHtmlDouble() { 27 | $tpl = $this->smarty->createTemplate('eval:{"I\'m some to ä be \"escaped\" or ©"|escape:"html":null:false}'); 28 | $this->assertEquals("I'm some <html> to ä be "escaped" or ©", $this->smarty->fetch($tpl)); 29 | } 30 | 31 | public function testUrl() { 32 | $tpl = $this->smarty->createTemplate('eval:{"http://some.encoded.com/url?parts#foo"|escape:"url"}'); 33 | $this->assertEquals("http%3A%2F%2Fsome.encoded.com%2Furl%3Fparts%23foo", $this->smarty->fetch($tpl)); 34 | } 35 | 36 | public function testUrlpathinfo() { 37 | $tpl = $this->smarty->createTemplate('eval:{"http://some.encoded.com/url?parts#foo"|escape:"urlpathinfo"}'); 38 | $this->assertEquals("http%3A//some.encoded.com/url%3Fparts%23foo", $this->smarty->fetch($tpl)); 39 | } 40 | 41 | public function testHex() { 42 | $tpl = $this->smarty->createTemplate('eval:{"a/cäa"|escape:"hex"}'); 43 | $this->assertEquals("%61%2f%63%c3%a4%61", $this->smarty->fetch($tpl)); 44 | } 45 | 46 | public function testJavascript() { 47 | $tpl = $this->smarty->createTemplate('eval:{"var x = { foo : \"bar\n\" };"|escape:"javascript"}'); 48 | $this->assertEquals("var x = { foo : \\\"bar\\n\\\" };", $this->smarty->fetch($tpl)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifiercompiler.escape.php: -------------------------------------------------------------------------------- 1 | 11 | * Name: escape
12 | * Purpose: escape string for output 13 | * 14 | * @link http://www.smarty.net/docsv2/en/language.modifier.escape 15 | * @author Rodney Rehm 16 | * @param array $params parameters 17 | * @return string with compiled code 18 | */ 19 | function smarty_modifiercompiler_escape($params, $compiler) 20 | { 21 | try { 22 | $esc_type = isset($params[1]) ? \Box\Brainy\Compiler\Decompile::decompileString($params[1]) : 'html'; 23 | $double_encode = isset($params[3]) ? $params[3] : 'true'; 24 | 25 | switch ($esc_type) { 26 | case 'html': 27 | return 'htmlspecialchars(' . $params[0] .', ENT_QUOTES, \'UTF-8\', ' . $double_encode . ')'; 28 | 29 | case 'url': 30 | return 'rawurlencode(' . $params[0] . ')'; 31 | 32 | case 'urlpathinfo': 33 | return 'str_replace("%2F", "/", rawurlencode(' . $params[0] . '))'; 34 | 35 | case 'quotes': 36 | // escape unescaped single quotes 37 | return 'preg_replace("%(? "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", "\\n" => "\\\n", " "<\/" ))'; 42 | 43 | } 44 | } catch (\Box\Brainy\Exceptions\SmartyException $e) { 45 | // pass through to regular plugin fallback 46 | } 47 | 48 | // could not optimize |escape call, so fallback to regular plugin 49 | $compiler->template->required_plugins['compiled']['escape']['modifier']['file'] = BRAINY_PLUGINS_DIR .'modifier.escape.php'; 50 | $compiler->template->required_plugins['compiled']['escape']['modifier']['function'] = 'smarty_modifier_escape'; 51 | 52 | return 'smarty_modifier_escape(' . join(', ', $params) . ')'; 53 | } 54 | -------------------------------------------------------------------------------- /src/Brainy/Resources/ResourceString.php: -------------------------------------------------------------------------------- 1 | uid = $source->filepath = sha1($source->name); 28 | $source->timestamp = 0; 29 | $source->exists = true; 30 | } 31 | 32 | /** 33 | * Load template's source from $resource_name into current template object 34 | * 35 | * @uses decode() to decode base64 and urlencoded template_resources 36 | * @param TemplateSource $source source object 37 | * @return string template source 38 | */ 39 | public function getContent(TemplateSource $source) 40 | { 41 | return $source->name; 42 | } 43 | 44 | /** 45 | * modify resource_name according to resource handlers specifications 46 | * 47 | * @param \Box\Brainy\Brainy $smarty Smarty instance 48 | * @param string $resource_name resource_name to make unique 49 | * @return string unique resource name 50 | */ 51 | protected function buildUniqueResourceName(\Box\Brainy\Brainy $smarty, $resource_name) 52 | { 53 | return get_class($this) . '#' . $resource_name; 54 | } 55 | 56 | /** 57 | * Determine basename for compiled filename 58 | * 59 | * Always returns an empty string. 60 | * 61 | * @param TemplateSource $source source object 62 | * @return string resource's basename 63 | */ 64 | protected function getBasename(TemplateSource $source) 65 | { 66 | return ''; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.escape.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: escape
14 | * Purpose: escape string for output 15 | * 16 | * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual) 17 | * @author Monte Ohrt 18 | * @param string $string input string 19 | * @param string $esc_type escape type 20 | * @param string $char_set character set, used for htmlspecialchars() or htmlentities() 21 | * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() 22 | * @return string escaped input string 23 | */ 24 | function smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'UTF-8', $double_encode = true) 25 | { 26 | switch ($esc_type) { 27 | case 'html': 28 | return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); 29 | 30 | case 'url': 31 | return rawurlencode($string); 32 | 33 | case 'urlpathinfo': 34 | return str_replace('%2F', '/', rawurlencode($string)); 35 | 36 | case 'quotes': 37 | // escape unescaped single quotes 38 | return preg_replace("%(? '\\\\', "'" => "\\'", '"' => '\\"', "\r" => '\\r', "\n" => '\\n', ' '<\/')); 54 | 55 | default: 56 | throw new Exception('Unrecognized escape option "' . $esc_type . '"'); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/CommentsTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate("eval:{* this is a comment *}"); 19 | $this->assertEquals("", $this->smarty->fetch($tpl)); 20 | } 21 | public function testSimpleComment2() { 22 | $tpl = $this->smarty->createTemplate("eval:{* another \$foo comment *}"); 23 | $this->assertEquals("", $this->smarty->fetch($tpl)); 24 | } 25 | public function testSimpleComment3() { 26 | $tpl = $this->smarty->createTemplate("eval:{* another comment *}some in between{* another comment *}"); 27 | $this->assertEquals("some in between", $this->smarty->fetch($tpl)); 28 | } 29 | public function testSimpleComment4() { 30 | $tpl = $this->smarty->createTemplate("eval:{* multi line \n comment *}"); 31 | $this->assertEquals("", $this->smarty->fetch($tpl)); 32 | } 33 | public function testSimpleComment5() { 34 | $tpl = $this->smarty->createTemplate("eval:{* /* foo * / *}"); 35 | $this->assertEquals("", $this->smarty->fetch($tpl)); 36 | } 37 | /** 38 | * test comment text combinations 39 | */ 40 | public function testTextComment1() { 41 | $tpl = $this->smarty->createTemplate("eval:A{* comment *}B\nC"); 42 | $this->assertEquals("AB\nC", $this->smarty->fetch($tpl)); 43 | } 44 | public function testTextComment2() { 45 | $tpl = $this->smarty->createTemplate("eval:D{* comment *}\n{* comment *}E\nF"); 46 | $this->assertEquals("D\nE\nF", $this->smarty->fetch($tpl)); 47 | } 48 | public function testTextComment3() { 49 | $tpl = $this->smarty->createTemplate("eval:G{* multi \nline *}H"); 50 | $this->assertEquals("GH", $this->smarty->fetch($tpl)); 51 | } 52 | public function testTextComment4() { 53 | $tpl = $this->smarty->createTemplate("eval:I{* multi \nline *}\nJ"); 54 | $this->assertEquals("I\nJ", $this->smarty->fetch($tpl)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructPrintExpression.php: -------------------------------------------------------------------------------- 1 | smarty->default_modifiers)) { 21 | if (empty($compiler->default_modifier_list)) { 22 | self::populateCompilerDefaultMethods($compiler); 23 | } 24 | $output = ConstructModifier::compileOpen( 25 | $compiler, 26 | array( 27 | 'value' => $output, 28 | 'modifierlist' => $compiler->default_modifier_list, 29 | ), 30 | null 31 | ); 32 | } 33 | 34 | // autoescape html 35 | if ($compiler->template->smarty->escape_html) { 36 | $output = "htmlspecialchars({$output}, ENT_QUOTES, 'UTF-8')"; 37 | } 38 | 39 | return "echo $output;\n"; 40 | } 41 | 42 | /** 43 | * Populates the default_modifier_list member of the compiler instance 44 | * @param \Box\Brainy\Compiler\TemplateCompiler $compiler 45 | * @return void 46 | */ 47 | private static function populateCompilerDefaultMethods($compiler) 48 | { 49 | $modifierlist = array(); 50 | foreach ($compiler->smarty->default_modifiers as $key => $single_default_modifier) { 51 | preg_match_all('/(\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|:|[^:]+)/', $single_default_modifier, $mod_array); 52 | for ($i = 0, $count = count($mod_array[0]); $i < $count; $i++) { 53 | if ($mod_array[0][$i] !== ':') { 54 | $modifierlist[$key][] = $mod_array[0][$i]; 55 | } 56 | } 57 | } 58 | $compiler->default_modifier_list = $modifierlist; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructFunction.php: -------------------------------------------------------------------------------- 1 | $default) { 22 | if ($argName === 'name') { 23 | continue; 24 | } 25 | $safeArgName = var_export($argName, true); 26 | $paramConstructionArray[] = " if (!isset(\$tmp->tpl_vars[$safeArgName])) \$tmp->setVariable($safeArgName, $default);\n"; 27 | } 28 | 29 | $output = "\$_smarty_tpl->tpl_vars['smarty']['functions'][" . $name . "] = function (\$params) use (\$_smarty_tpl) {\n"; 30 | // We pass `true` to $useRootScope because it avoids doing an iteration 31 | // over the whole scope. It's a sloppy optimization but it saves good 32 | // time. 33 | $output .= " \$tmp = new \\Box\\Brainy\\Templates\\TemplateBase(\$_smarty_tpl->smarty, true);\n"; 34 | $output .= " \$tmp->parent = &\$_smarty_tpl;\n"; 35 | $output .= " \$tmp->tpl_vars = \$_smarty_tpl->tpl_vars;\n"; // assign by value for arrays 36 | $output .= " \$tmp->tpl_vars['smarty'] = &\$_smarty_tpl->tpl_vars['smarty'];\n"; 37 | $output .= " \$tmp->applyDataFrom(\$params);\n"; 38 | $output .= implode('', $paramConstructionArray); 39 | $output .= " \$_smarty_tpl = \$tmp;\n"; 40 | 41 | return $output; 42 | } 43 | 44 | /** 45 | * @param \Box\Brainy\Compiler\TemplateCompiler $compiler A compiler reference 46 | * @param array|null $args Arguments 47 | * @return mixed 48 | */ 49 | public static function compileClose(\Box\Brainy\Compiler\TemplateCompiler $compiler, $args) 50 | { 51 | self::closeTag($compiler, array('function')); 52 | return "};\n"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/ClearAllAssignTest.php: -------------------------------------------------------------------------------- 1 | smarty->safe_lookups = \Box\Brainy\Brainy::LOOKUP_SAFE; 22 | $this->smartyBC->safe_lookups = \Box\Brainy\Brainy::LOOKUP_SAFE; 23 | 24 | $this->smarty->assign('foo', 'foo'); 25 | $this->_data = new Helpers\Data($this->smarty); 26 | $this->_data->assign('bar', 'bar'); 27 | $this->_tpl = $this->smarty->createTemplate('eval:{$foo}{$bar}{$blar}', null, $this->_data); 28 | $this->_tpl->assign('blar', 'blar'); 29 | 30 | $this->smartyBC->assign('foo', 'foo'); 31 | $this->_dataBC = new Helpers\Data($this->smartyBC); 32 | $this->_dataBC->assign('bar', 'bar'); 33 | $this->_tplBC = $this->smartyBC->createTemplate('eval:{$foo}{$bar}{$blar}', null, $this->_dataBC); 34 | $this->_tplBC->assign('blar', 'blar'); 35 | } 36 | 37 | /** 38 | * test all variables accessable 39 | */ 40 | public function testAllVariablesAccessible() { 41 | $this->assertEquals('foobarblar', $this->smarty->fetch($this->_tpl)); 42 | } 43 | 44 | /** 45 | * test clear all assign in template 46 | */ 47 | public function testClearAllAssignInTemplate() { 48 | $this->_tpl->clearAllAssign(); 49 | $this->assertEquals('foobar', $this->smarty->fetch($this->_tpl)); 50 | } 51 | 52 | /** 53 | * Clearing all on Brainy shouldn't affect the tempalte once the template 54 | * is already created. There should be no logical connection between the 55 | * Brainy instance's root scope and the template instance's root scope. 56 | */ 57 | public function testClearAllAssignInSmarty() { 58 | $this->smarty->clearAllAssign(); 59 | $this->assertEquals('foobarblar', $this->smarty->fetch($this->_tpl)); 60 | } 61 | public function testSmarty2ClearAllAssignInSmarty() { 62 | $this->smartyBC->clear_all_assign(); 63 | $this->assertEquals('foobarblar', $this->smartyBC->fetch($this->_tplBC)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Wrappers/StaticWrapper.php: -------------------------------------------------------------------------------- 1 | code = $code; 15 | } 16 | 17 | /** 18 | * The __toString() method un-statics the contents 19 | * @return string 20 | */ 21 | public function __toString() 22 | { 23 | return (string) $this->code; 24 | } 25 | 26 | /** 27 | * Combine two values which may be static wrappers. The output is only a 28 | * static wrapper if both the left and the right are static wrappers. 29 | * @param string|StaticWrapper $left 30 | * @param string|StaticWrapper $right 31 | * @return string|StaticWrapper 32 | */ 33 | public static function concat($left, $right) 34 | { 35 | if ($left instanceof StaticWrapper && $right instanceof StaticWrapper) { 36 | return new StaticWrapper($left . $right); 37 | } 38 | return $left . $right; 39 | } 40 | 41 | /** 42 | * Combine two values which may be static wrappers. The output is a static 43 | * wrapper if either the left or the right are static wrappers. This is 44 | * only to be used in cases where the left or the right is known not to 45 | * affect whether the result is a static wrapper. 46 | * @param string|StaticWrapper $left 47 | * @param string|StaticWrapper $right 48 | * @return string|StaticWrapper 49 | */ 50 | public static function staticConcat($left, $right) 51 | { 52 | if ($left instanceof StaticWrapper || $right instanceof StaticWrapper) { 53 | return new StaticWrapper($left . $right); 54 | } 55 | return $left . $right; 56 | } 57 | 58 | /** 59 | * If all of the values in $conditions are static wrappers, the result is a 60 | * static wrapper of $code. Otherwise, $code is returned. 61 | * @param string $code 62 | * @param array $conditions 63 | * @return string|StaticWrapper 64 | */ 65 | public static function staticIfAll($code, $conditions) 66 | { 67 | foreach ($conditions as $cond) { 68 | if (!($cond instanceof StaticWrapper)) { 69 | return $code; 70 | } 71 | } 72 | return new StaticWrapper($code); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/SingleQuotedStringTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:This isn\'t part of a string'); 20 | $this->assertEquals('This isn\'t part of a string', $this->smarty->fetch($tpl)); 21 | } 22 | 23 | /** 24 | * test simple single quoted string 25 | */ 26 | public function testSimpleSingleQuotedString() { 27 | $tpl = $this->smarty->createTemplate('eval:{$foo=\'Hello World\'}{$foo}'); 28 | $this->assertEquals('Hello World', $this->smarty->fetch($tpl)); 29 | } 30 | /** 31 | * test that tags not interpreted in single quoted strings 32 | */ 33 | public function testTagsInSingleQuotedString() { 34 | $tpl = $this->smarty->createTemplate('eval:{$foo=\'Hello {1+2} World\'}{$foo}'); 35 | $this->assertEquals('Hello {1+2} World', $this->smarty->fetch($tpl)); 36 | } 37 | /** 38 | * test that vars not interpreted in single quoted strings 39 | */ 40 | public function testVarsInSingleQuotedString() { 41 | $tpl = $this->smarty->createTemplate('eval:{$foo=\'Hello $bar World\'}{$foo}'); 42 | $this->assertEquals('Hello $bar World', $this->smarty->fetch($tpl)); 43 | } 44 | /** 45 | * test double quotes in single quoted strings 46 | */ 47 | public function testDoubleQuotesInSingleQuotedString() { 48 | $tpl = $this->smarty->createTemplate('eval:{$foo=\'Hello "World"\'}{$foo}'); 49 | $this->assertEquals('Hello "World"', $this->smarty->fetch($tpl)); 50 | } 51 | /** 52 | * test escaped single quotes in single quoted strings 53 | */ 54 | public function testEscapedSingleQuotesInSingleQuotedString() { 55 | $tpl = $this->smarty->createTemplate('eval:{$foo=\'Hello \\\'World\'}{$foo}'); 56 | $this->assertEquals("Hello 'World", $this->smarty->fetch($tpl)); 57 | } 58 | /** 59 | * test empty single quoted strings 60 | */ 61 | public function testEmptySingleQuotedString() { 62 | $tpl = $this->smarty->createTemplate('eval:{$foo=\'\'}{$foo}'); 63 | $this->assertEquals("", $this->smarty->fetch($tpl)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/PluginModifierTruncateTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate}'); 17 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 18 | } 19 | 20 | public function testLength() { 21 | $result = 'Two Sisters Reunite after...'; 22 | $tpl = $this->smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate:30}'); 23 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 24 | } 25 | 26 | public function testEtc() { 27 | $result = 'Two Sisters Reunite after'; 28 | $tpl = $this->smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate:30:""}'); 29 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 30 | } 31 | 32 | public function testEtc2() { 33 | $result = 'Two Sisters Reunite after---'; 34 | $tpl = $this->smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate:30:"---"}'); 35 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 36 | } 37 | 38 | public function testBreak() { 39 | $result = 'Two Sisters Reunite after Eigh'; 40 | $tpl = $this->smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate:30:"":true}'); 41 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 42 | } 43 | 44 | public function testBreak2() { 45 | $result = 'Two Sisters Reunite after E...'; 46 | $tpl = $this->smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate:30:"...":true}'); 47 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 48 | } 49 | 50 | public function testMiddle() { 51 | $result = 'Two Sisters Re..ckout Counter.'; 52 | $tpl = $this->smarty->createTemplate('eval:{"Two Sisters Reunite after Eighteen Years at Checkout Counter."|truncate:30:"..":true:true}'); 53 | $this->assertEquals($result, $this->smarty->fetch($tpl)); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.date_format.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: date_format
14 | * Purpose: format datestamps via strftime
15 | * Input:
16 | * - string: input date string 17 | * - format: strftime format for output 18 | * - default_date: default date if $string is empty 19 | * 20 | * @link http://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual) 21 | * @author Monte Ohrt 22 | * @param string $string input date string 23 | * @param string $format strftime format for output 24 | * @param string $default_date default date if $string is empty 25 | * @param string $formatter either 'strftime' or 'auto' 26 | * @return string |void 27 | * @uses smarty_make_timestamp() 28 | */ 29 | function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') 30 | { 31 | if ($format === null) { 32 | $format = \Box\Brainy\Brainy::$_DATE_FORMAT; 33 | } 34 | /** 35 | * Include the {@link shared.make_timestamp.php} plugin 36 | */ 37 | include_once BRAINY_PLUGINS_DIR . 'shared.make_timestamp.php'; 38 | if ($string != '' && $string != '0000-00-00' && $string != '0000-00-00 00:00:00') { 39 | $timestamp = smarty_make_timestamp($string); 40 | } elseif ($default_date != '') { 41 | $timestamp = smarty_make_timestamp($default_date); 42 | } else { 43 | return; 44 | } 45 | if ($formatter=='strftime' || ($formatter=='auto'&&strpos($format, '%')!==false)) { 46 | if (DIRECTORY_SEPARATOR == '\\') { 47 | $_win_from = array('%D', '%h', '%n', '%r', '%R', '%t', '%T'); 48 | $_win_to = array('%m/%d/%y', '%b', "\n", '%I:%M:%S %p', '%H:%M', "\t", '%H:%M:%S'); 49 | if (strpos($format, '%e') !== false) { 50 | $_win_from[] = '%e'; 51 | $_win_to[] = sprintf('%\' 2d', date('j', $timestamp)); 52 | } 53 | if (strpos($format, '%l') !== false) { 54 | $_win_from[] = '%l'; 55 | $_win_to[] = sprintf('%\' 2d', date('h', $timestamp)); 56 | } 57 | $format = str_replace($_win_from, $_win_to, $format); 58 | } 59 | 60 | return strftime($format, $timestamp); 61 | } else { 62 | return date($format, $timestamp); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/DelimiterTest.php: -------------------------------------------------------------------------------- 1 | delimiter 16 | */ 17 | public function testDelimiter1() { 18 | $this->smarty->left_delimiter = '<{'; 19 | $this->smarty->right_delimiter = '}>'; 20 | $tpl = $this->smarty->createTemplate('eval:<{* comment *}><{if true}><{\'hello world\'}><{/if}>'); 21 | $this->assertEquals("hello world", $this->smarty->fetch($tpl)); 22 | } 23 | /** 24 | * test <-{ }-> delimiter 25 | */ 26 | public function testDelimiter2() { 27 | $this->smarty->left_delimiter = '<-{'; 28 | $this->smarty->right_delimiter = '}->'; 29 | $tpl = $this->smarty->createTemplate('eval:<-{* comment *}-><-{if true}-><-{\'hello world\'}-><-{/if}->'); 30 | $this->assertEquals("hello world", $this->smarty->fetch($tpl)); 31 | } 32 | /** 33 | * test <--{ }--> delimiter 34 | */ 35 | public function testDelimiter3() { 36 | $this->smarty->left_delimiter = '<--{'; 37 | $this->smarty->right_delimiter = '}-->'; 38 | $tpl = $this->smarty->createTemplate('eval:<--{* comment *}--><--{if true}--><--{\'hello world\'}--><--{/if}-->'); 39 | $this->assertEquals("hello world", $this->smarty->fetch($tpl)); 40 | } 41 | /** 42 | * test {{ }} delimiter 43 | */ 44 | public function testDelimiter4() { 45 | $this->smarty->left_delimiter = '{{'; 46 | $this->smarty->right_delimiter = '}}'; 47 | $tpl = $this->smarty->createTemplate('eval:{{* comment *}}{{if true}}{{\'hello world\'}}{{/if}}'); 48 | $this->assertEquals("hello world", $this->smarty->fetch($tpl)); 49 | } 50 | /** 51 | * test {= =} delimiter for conficts with option flags 52 | */ 53 | public function testDelimiter5() { 54 | $this->smarty->left_delimiter = '{='; 55 | $this->smarty->right_delimiter = '=}'; 56 | $tpl = $this->smarty->createTemplate('eval:{=assign var=foo value=\'hello world\'=}{=$foo=}'); 57 | $this->assertEquals("hello world", $this->smarty->fetch($tpl)); 58 | } 59 | 60 | public function testSmartyVars() { 61 | $this->smarty->left_delimiter = '{='; 62 | $this->smarty->right_delimiter = '=}'; 63 | $tpl = $this->smarty->createTemplate('eval:{=$smarty.ldelim=}{=$smarty.rdelim=}'); 64 | $this->assertEquals("{==}", $this->smarty->fetch($tpl)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/CompileFunctionTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('test_template_function_tag1.tpl'); 19 | $this->assertEquals("default param", $this->smarty->fetch($tpl)); 20 | } 21 | /** 22 | * test simple function call tag 2 23 | */ 24 | public function testSimpleFunction2() { 25 | $tpl = $this->smarty->createTemplate('test_template_function_tag2.tpl'); 26 | $this->assertEquals("default param default param2", $this->smarty->fetch($tpl)); 27 | } 28 | /** 29 | * test overwrite default function call tag 30 | */ 31 | public function testOverwriteDefaultFunction() { 32 | $tpl = $this->smarty->createTemplate('test_template_function_tag3.tpl'); 33 | $this->assertEquals("overwrite param default param2", $this->smarty->fetch($tpl)); 34 | } 35 | /** 36 | * test recursive function call tag 37 | */ 38 | public function testRecursiveFunction() { 39 | $tpl = $this->smarty->createTemplate('test_template_function_tag4.tpl'); 40 | $this->assertEquals("012345", $this->smarty->fetch($tpl)); 41 | } 42 | /** 43 | * test inherited function call tag 44 | */ 45 | public function testInheritedFunction() { 46 | $tpl = $this->smarty->createTemplate('test_template_function_tag5.tpl'); 47 | $this->assertEquals("012345", $this->smarty->fetch($tpl)); 48 | } 49 | /** 50 | * test function definition in include 51 | */ 52 | public function testDefineFunctionInclude() { 53 | $tpl = $this->smarty->createTemplate('test_template_function_tag6.tpl'); 54 | $this->assertEquals("012345", $this->smarty->fetch($tpl)); 55 | } 56 | /** 57 | * test external function definition 58 | */ 59 | public function testExternalDefinedFunction() { 60 | $tpl = $this->smarty->createTemplate('string:{include file="template_function_lib.tpl"}{call name=template_func1}'); 61 | $tpl->assign('foo', 'foo'); 62 | $this->assertStringContainsString('foo', $this->smarty->fetch($tpl)); 63 | } 64 | 65 | 66 | public function testIncludesInFunction() 67 | { 68 | $out = $this->smarty->fetch('eval:{function name="foo"}{include file="eval:temp"}{/function}{call name="foo"}'); 69 | $this->assertEquals('temp', $out); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.wordwrap.php: -------------------------------------------------------------------------------- 1 | $width) { 26 | // remove last space 27 | $t = mb_substr($t, 0, -1, 'UTF-8'); 28 | $_previous = false; 29 | $length = 0; 30 | 31 | if ($cut) { 32 | $_tokens = preg_split('!(.{' . $width . '})!Su', $_token, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE); 33 | // broken words go on a new line 34 | $t .= $break; 35 | } 36 | } 37 | 38 | foreach ($_tokens as $token) { 39 | $_space = (bool) preg_match('!^\s$!Su', $token); 40 | $token_length = mb_strlen($token, 'UTF-8'); 41 | $length += $token_length; 42 | 43 | if ($length > $width) { 44 | // remove space before inserted break 45 | if ($_previous && $token_length < $width) { 46 | $t = mb_substr($t, 0, -1, 'UTF-8'); 47 | } 48 | 49 | // add the break before the token 50 | $t .= $break; 51 | $length = $token_length; 52 | 53 | // skip space after inserting a break 54 | if ($_space) { 55 | $length = 0; 56 | continue; 57 | } 58 | } elseif ($token == "\n") { 59 | // hard break must reset counters 60 | $_previous = 0; 61 | $length = 0; 62 | } else { 63 | // remember if we had a space or not 64 | $_previous = $_space; 65 | } 66 | // add the token 67 | $t .= $token; 68 | } 69 | } 70 | 71 | return $t; 72 | } 73 | -------------------------------------------------------------------------------- /test/DefaultAssignScopeTest.php: -------------------------------------------------------------------------------- 1 | smarty->fetch('eval:{assign var="foo" value="bar"}'); 17 | $this->assertEquals( 18 | $this->smarty->getTemplateVars('foo'), 19 | null, 20 | 'Should not have access to local vars' 21 | ); 22 | } 23 | 24 | public function testRootScopeGivesAccessToVars() { 25 | \Box\Brainy\Brainy::$default_assign_scope = \Box\Brainy\Brainy::SCOPE_ROOT; 26 | $this->smarty->fetch('eval:{assign var="foo" value="bar"}'); 27 | $this->assertEquals( 28 | $this->smarty->getTemplateVars('foo'), 29 | 'bar', 30 | 'Should have access to vars' 31 | ); 32 | } 33 | 34 | public function testGlobalScopeGivesAccessToVars() { 35 | \Box\Brainy\Brainy::$default_assign_scope = \Box\Brainy\Brainy::SCOPE_GLOBAL; 36 | $this->smarty->fetch('eval:{assign var="foo" value="bar"}'); 37 | $this->assertEquals( 38 | $this->smarty->getTemplateVars('foo'), 39 | 'bar', 40 | 'Should have access to vars' 41 | ); 42 | } 43 | 44 | public function testDefaultScopeCanBeOverridden() { 45 | \Box\Brainy\Brainy::$default_assign_scope = \Box\Brainy\Brainy::SCOPE_GLOBAL; 46 | $this->smarty->fetch('eval:{assign var="foo" value="bar" scope="local"}'); 47 | $this->assertEquals( 48 | $this->smarty->getTemplateVars('foo'), 49 | null, 50 | 'Should not have access to local vars' 51 | ); 52 | } 53 | 54 | /* Generic test for functions that use the template `assign()` method */ 55 | 56 | public function testDefaultIsLocalScopeForCapture() { 57 | $this->smarty->fetch('eval:{capture assign="foo"}captured{/capture}'); 58 | $this->assertEquals( 59 | $this->smarty->getTemplateVars('foo'), 60 | null, 61 | 'Should not have access to local vars' 62 | ); 63 | } 64 | 65 | public function testRootScopeGivesAccessToVarsForCapture() { 66 | \Box\Brainy\Brainy::$default_assign_scope = \Box\Brainy\Brainy::SCOPE_ROOT; 67 | $this->smarty->fetch('eval:{capture assign="foo"}captured{/capture}'); 68 | $this->assertEquals( 69 | $this->smarty->getTemplateVars('foo'), 70 | 'captured', 71 | 'Should have access to vars' 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Brainy/plugins/modifier.capitalize.php: -------------------------------------------------------------------------------- 1 | 13 | * Name: capitalize
14 | * Purpose: capitalize words in the string 15 | * 16 | * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }} 17 | * 18 | * @param string $string string to capitalize 19 | * @param boolean $uc_digits also capitalize "x123" to "X123" 20 | * @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa" 21 | * @return string capitalized string 22 | * @author Monte Ohrt 23 | * @author Rodney Rehm 24 | */ 25 | function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) 26 | { 27 | if ($lc_rest) { 28 | // uppercase (including hyphenated words) 29 | $upper_string = mb_convert_case($string, MB_CASE_TITLE, 'UTF-8'); 30 | } else { 31 | // uppercase word breaks 32 | $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!Su", 'smarty_mod_cap_mbconvert_cb', $string); 33 | } 34 | // check uc_digits case 35 | if (!$uc_digits) { 36 | if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!u", $string, $matches, PREG_OFFSET_CAPTURE)) { 37 | foreach ($matches[1] as $match) { 38 | $upper_string = substr_replace($upper_string, mb_strtolower($match[0], 'UTF-8'), $match[1], strlen($match[0])); 39 | } 40 | } 41 | } 42 | $upper_string = preg_replace_callback("!((^|\s)['\"])(\w)!u", 'smarty_mod_cap_mbconvert2_cb', $upper_string); 43 | return $upper_string; 44 | } 45 | 46 | /* 47 | * 48 | * Bug: create_function() use exhausts memory when used in long loops 49 | * Fix: use declared functions for callbacks instead of using create_function() 50 | * Note: This can be fixed using anonymous functions instead, but that requires PHP >= 5.3 51 | * 52 | * @author Kyle Renfrow 53 | */ 54 | function smarty_mod_cap_mbconvert_cb($matches) 55 | { 56 | return stripslashes($matches[1]).mb_convert_case(stripslashes($matches[2]), MB_CASE_UPPER, 'UTF-8'); 57 | } 58 | 59 | function smarty_mod_cap_mbconvert2_cb($matches) 60 | { 61 | return stripslashes($matches[1]).mb_convert_case(stripslashes($matches[3]), MB_CASE_UPPER, 'UTF-8'); 62 | } 63 | 64 | function smarty_mod_cap_ucfirst_cb($matches) 65 | { 66 | return stripslashes($matches[1]).ucfirst(stripslashes($matches[2])); 67 | } 68 | 69 | function smarty_mod_cap_ucfirst2_cb($matches) 70 | { 71 | return stripslashes($matches[1]).ucfirst(stripslashes($matches[3])); 72 | } 73 | -------------------------------------------------------------------------------- /test/DoubleQuotedStringTest.php: -------------------------------------------------------------------------------- 1 | smarty->createTemplate('eval:{$foo="Hello World"}{$foo}'); 20 | $this->assertEquals('Hello World', $this->smarty->fetch($tpl)); 21 | } 22 | /** 23 | * test expression tags in double quoted strings 24 | */ 25 | public function testTagsInDoubleQuotedString() { 26 | $tpl = $this->smarty->createTemplate('eval:{$foo="Hello {1+2} World"}{$foo}'); 27 | $this->assertEquals('Hello 3 World', $this->smarty->fetch($tpl)); 28 | } 29 | /** 30 | * test vars in double quoted strings 31 | */ 32 | public function testVarsInDoubleQuotedString1() { 33 | $tpl = $this->smarty->createTemplate('eval:{$bar=\'blah\'}{$foo="Hello $bar World"}{$foo}'); 34 | $this->assertEquals('Hello blah World', $this->smarty->fetch($tpl)); 35 | } 36 | public function testVarsInDoubleQuotedString2() { 37 | $tpl = $this->smarty->createTemplate('eval:{$bar=\'blah\'}{$buh=\'buh\'}{$foo="Hello $bar$buh World"}{$foo}'); 38 | $this->assertEquals('Hello blahbuh World', $this->smarty->fetch($tpl)); 39 | } 40 | /** 41 | * test vars in delimiter in double quoted strings 42 | */ 43 | public function testVarsDelimiterInDoubleQuotedString() { 44 | $tpl = $this->smarty->createTemplate('eval:{$bar=\'blah\'}{$foo="Hello {$bar}.test World"}{$foo}'); 45 | $this->assertEquals('Hello blah.test World', $this->smarty->fetch($tpl)); 46 | } 47 | /** 48 | * test escaped quotes in double quoted strings 49 | */ 50 | public function testEscapedQuotesInDoubleQuotedString() { 51 | $tpl = $this->smarty->createTemplate('eval:{$foo="Hello \" World"}{$foo}'); 52 | $this->assertEquals('Hello " World', $this->smarty->fetch($tpl)); 53 | } 54 | 55 | /** 56 | * test single quotes in double quoted strings 57 | */ 58 | public function testSingleQuotesInDoubleQuotedString() { 59 | $tpl = $this->smarty->createTemplate('eval:{$foo="Hello \'World\'"}{$foo}'); 60 | $this->assertEquals("Hello 'World'", $this->smarty->fetch($tpl)); 61 | } 62 | /** 63 | * test empty double quoted strings 64 | */ 65 | public function testEmptyDoubleQuotedString() { 66 | $tpl = $this->smarty->createTemplate('eval:{$foo=""}{$foo}'); 67 | $this->assertEquals("", $this->smarty->fetch($tpl)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/CompileForeachTest.php: -------------------------------------------------------------------------------- 1 | smarty->safe_lookups = \Box\Brainy\Brainy::LOOKUP_SAFE; 10 | } 11 | 12 | public function dataProviderForForEachLoops() 13 | { 14 | return array( 15 | array('{assign var=foo value=[0,1,2,3,4,5,6,7,8,9]}{foreach item=x from=$foo}{$x}{/foreach}', "0123456789"), 16 | array('{assign var=foo value=[0,1,2,3,4,5,6,7,8,9]}{foreach item=x from=$foo}{if $x == 2}{break}{/if}{$x}{/foreach}', "01"), 17 | array('{assign var=foo value=[0,1,2,3,4,5,6,7,8,9]}{foreach item=x from=$foo}{if $x == 2}{continue}{/if}{$x}{/foreach}', "013456789"), 18 | array('{assign var=foo value=[0,1,2,3,4,5,6,7,8,9]}{foreach item=x from=$foo}{$x}{foreachelse}else{/foreach}', "0123456789"), 19 | array('{foreach item=x from=$foo}{$x}{foreachelse}else{/foreach}', "else"), 20 | array('{foreach item=x key=y from=[9,8,7,6,5,4,3,2,1,0]}{$y}{$x}{foreachelse}else{/foreach}', "09182736455463728190"), 21 | array('{foreach item=x name=foo from=[0,1,2,3,4,5,6,7,8,9]}{$x}{foreachelse}else{/foreach}total{$smarty.foreach.foo.total}', "0123456789total10"), 22 | array('{foreach item=x name=foo from=[0,1,2,3,4,5,6,7,8,9]}{$smarty.foreach.foo.index}{$smarty.foreach.foo.iteration}{foreachelse}else{/foreach}', "011223344556677889910"), 23 | array('{foreach item=x name=foo from=[0,1,2,3,4,5,6,7,8,9]}{if $smarty.foreach.foo.first}first{/if}{if $smarty.foreach.foo.last}last{/if}{$x}{foreachelse}else{/foreach}', "first012345678last9"), 24 | array('{foreach item=x name=foo from=[0,1]}{$x}{foreachelse}else{/foreach}{if $smarty.foreach.foo.show}show{else}noshow{/if}', "01show"), 25 | array('{foreach item=x name=foo from=[]}{$x}{foreachelse}else{/foreach}{if $smarty.foreach.foo.show}show{else} noshow{/if}', "else noshow"), 26 | array('{assign var=foo value=[0,1,2,3,4,5,6,7,8,9]}{foreach $foo as $x}{$x}{/foreach}', "0123456789"), 27 | array('{assign var=foo value=[0,1,2,3,4,5,6,7,8,9]}{foreach $foo as $x}{$x}{foreachelse}else{/foreach}', "0123456789"), 28 | array('{foreach $foo as $x}{$x}{foreachelse}else{/foreach}', "else"), 29 | array('{assign var=foo value=[9,8,7,6,5,4,3,2,1,0]}{foreach $foo as $y=>$x}{$y}{$x}{foreachelse}else{/foreach}', "09182736455463728190"), 30 | ); 31 | } 32 | 33 | /** 34 | * @dataProvider dataProviderForForEachLoops 35 | */ 36 | public function testForEachLoops($template, $expected) 37 | { 38 | $this->assertEquals($expected, $this->smarty->fetch('eval:' . $template)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructAssign.php: -------------------------------------------------------------------------------- 1 | template->smarty instanceof SmartyBC && Brainy::$assignment_compat === Brainy::ASSIGN_COMPAT) { 25 | $output = "if (isset(\$_smarty_tpl->tpl_vars[$var])) {\n"; 26 | $output .= " \$_smarty_tpl->tpl_vars[$var] = &$value;\n"; 27 | $output .= "} else {\$_smarty_tpl->setVariable($var, $value, $scope);}\n"; 28 | } else { 29 | $output = "\$_smarty_tpl->setVariable($var, $value, $scope);\n"; 30 | } 31 | return $output; 32 | } 33 | 34 | /** 35 | * Returns the scope, given the raw scope string 36 | * @param string $raw 37 | * @param int|null|void $default The default scope, or null 38 | * @return int 39 | */ 40 | public static function getScope($args, $default = null) 41 | { 42 | $scopeRaw = self::getOptionalArg($args, 'scope', self::getDefaultScope($default)); 43 | switch (\Box\Brainy\Compiler\Decompile::decompileString($scopeRaw)) { 44 | case 'local': 45 | return Brainy::SCOPE_LOCAL; 46 | case 'parent': 47 | return Brainy::SCOPE_PARENT; 48 | case 'root': 49 | return Brainy::SCOPE_ROOT; 50 | case 'global': 51 | return Brainy::SCOPE_GLOBAL; 52 | default: 53 | $compiler->trigger_template_error('illegal value for "scope" attribute: ' . $scopeRaw, $compiler->lex->taglineno); 54 | } 55 | } 56 | 57 | /** 58 | * @param int|null|void $default 59 | * @return string 60 | */ 61 | private static function getDefaultScope($default) 62 | { 63 | switch ($default ?: Brainy::$default_assign_scope) { 64 | case Brainy::SCOPE_LOCAL: 65 | return '"local"'; 66 | case Brainy::SCOPE_PARENT: 67 | return '"parent"'; 68 | case Brainy::SCOPE_ROOT: 69 | return '"root"'; 70 | case Brainy::SCOPE_GLOBAL: 71 | return '"global"'; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Brainy/Compiler/Constructs/ConstructInclude.php: -------------------------------------------------------------------------------- 1 | assertIsNotStrict('Include shorthand is not allowed in strict mode. Use the file="" attribute instead.'); 22 | if (!isset($args[0])) { 23 | throw $e; 24 | } 25 | $file = $args[0]; 26 | } 27 | $file = (string) $file; 28 | $assign = self::getOptionalArg($args, 'assign'); 29 | $compileID = self::getOptionalArg( 30 | $args, 31 | 'compile_id', 32 | var_export($compiler->smarty->compile_id, true) 33 | ); 34 | $scope = ConstructAssign::getScope($args, Brainy::SCOPE_LOCAL); 35 | 36 | if (!$assign) { 37 | return self::getDisplayCode($file, $compileID, $scope, $args); 38 | } 39 | 40 | $output = 'ob_start();'; 41 | $output .= self::getDisplayCode($file, $compileID, $scope, $args); 42 | $output .= "\$_smarty_tpl->assign($assign, ob_get_clean(), $scope);\n"; 43 | return $output; 44 | 45 | } 46 | 47 | /** 48 | * Gets the PHP code to execute the included template 49 | * @param string $templatePath 50 | * @param string|null $compileID 51 | * @param int $scope 52 | * @return string 53 | */ 54 | protected static function getDisplayCode($templatePath, $compileID, $scope, $data) 55 | { 56 | if ($templatePath instanceof \Box\Brainy\Compiler\Helpers\ParseTree) { 57 | $templatePath = $templatePath->toSmartyPHP(); 58 | } 59 | 60 | $data = self::flattenCompiledArray($data); 61 | unset($data['assign']); 62 | unset($data['compile_id']); 63 | unset($data['file']); 64 | unset($data['inline']); 65 | unset($data['scope']); 66 | foreach ($data as $key => $value) { 67 | if (is_numeric($key)) { 68 | unset($data[$key]); 69 | } 70 | } 71 | 72 | return '$_smarty_tpl->renderSubTemplate(' . 73 | $templatePath . ', ' . 74 | ($compileID ?: null) . ', ' . 75 | self::exportArray($data, true) . ', ' . 76 | var_export($scope, true) . 77 | ");\n"; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Brainy/Runtime/Lookups.php: -------------------------------------------------------------------------------- 1 | smarty->addTemplateDir(dirname(__FILE__) .'/templates_2'); 17 | // note that 10 is a string! 18 | $this->smarty->addTemplateDir(dirname(__FILE__) .'/templates_3', '10'); 19 | $this->smarty->addTemplateDir(dirname(__FILE__) .'/templates_4', 'foo'); 20 | } 21 | 22 | protected function relative($path) { 23 | $path = str_replace( dirname(__FILE__), '.', $path ); 24 | if (DIRECTORY_SEPARATOR == "\\") { 25 | $path = str_replace( "\\", "/", $path ); 26 | } 27 | 28 | return $path; 29 | } 30 | 31 | public function testGetTemplateFilepath() { 32 | $tpl = $this->smarty->createTemplate('dirname.tpl'); 33 | $this->assertEquals('./templates/dirname.tpl', $this->relative($tpl->source->filepath)); 34 | } 35 | public function testGetTemplateFilepathNumber() { 36 | $tpl = $this->smarty->createTemplate('[1]dirname.tpl'); 37 | $this->assertEquals('./templates_2/dirname.tpl', $this->relative($tpl->source->filepath)); 38 | } 39 | public function testGetTemplateFilepathNumeric() { 40 | $tpl = $this->smarty->createTemplate('[10]dirname.tpl'); 41 | $this->assertEquals('./templates_3/dirname.tpl', $this->relative($tpl->source->filepath)); 42 | } 43 | public function testGetTemplateFilepathName() { 44 | $tpl = $this->smarty->createTemplate('[foo]dirname.tpl'); 45 | $this->assertEquals('./templates_4/dirname.tpl', $this->relative($tpl->source->filepath)); 46 | } 47 | 48 | public function testFetch() { 49 | $tpl = $this->smarty->createTemplate('dirname.tpl'); 50 | $this->assertEquals('templates', $this->smarty->fetch($tpl)); 51 | } 52 | public function testFetchNumber() { 53 | $tpl = $this->smarty->createTemplate('[1]dirname.tpl'); 54 | $this->assertEquals('templates_2', $this->smarty->fetch($tpl)); 55 | } 56 | public function testFetchNumeric() { 57 | $tpl = $this->smarty->createTemplate('[10]dirname.tpl'); 58 | $this->assertEquals('templates_3', $this->smarty->fetch($tpl)); 59 | } 60 | public function testFetchName() { 61 | $tpl = $this->smarty->createTemplate('[foo]dirname.tpl'); 62 | $this->assertEquals('templates_4', $this->smarty->fetch($tpl)); 63 | } 64 | 65 | public function testGetCompiledFilepath() { 66 | $tpl = $this->smarty->createTemplate('[foo]dirname.tpl'); 67 | $expected = './compiled/'.sha1($this->smarty->getTemplateDir('foo').'dirname.tpl').'.file.dirname.tpl.php'; 68 | $this->assertEquals($expected, $this->relative($tpl->compiled->filepath)); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | smarty = new \Box\Brainy\Brainy(); 17 | $this->setUpInstance($this->smarty); 18 | $this->smartyBC = new \Box\Brainy\SmartyBC(); 19 | $this->setUpInstance($this->smartyBC); 20 | 21 | \Box\Brainy\Brainy::$default_assign_scope = \Box\Brainy\Brainy::SCOPE_LOCAL; 22 | \Box\Brainy\Brainy::$enforce_expression_modifiers = array(); 23 | \Box\Brainy\Brainy::$global_tpl_vars = array(); 24 | \Box\Brainy\Resources\Resource::reset(); 25 | 26 | $this->clearFiles(); 27 | } 28 | 29 | protected function setUpInstance($smarty) 30 | { 31 | $smarty->setTemplateDir(realpath('test' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR)); 32 | $smarty->setCompileDir(realpath('test' . DIRECTORY_SEPARATOR . 'compiled' . DIRECTORY_SEPARATOR)); 33 | $smarty->setPluginsDir(BRAINY_PLUGINS_DIR); 34 | $smarty->tpl_vars = array(); 35 | $smarty->force_compile = false; 36 | $smarty->auto_literal = true; 37 | $smarty->registered_plugins = array(); 38 | $smarty->default_modifiers = array(); 39 | $smarty->escape_html = false; 40 | $smarty->use_sub_dirs = false; 41 | $smarty->security_policy = null; 42 | $smarty->left_delimiter = '{'; 43 | $smarty->right_delimiter = '}'; 44 | $smarty->enableSecurity(); 45 | $smarty->compile_id = null; 46 | $smarty->safe_lookups = \Box\Brainy\Brainy::LOOKUP_UNSAFE; 47 | } 48 | 49 | protected function clearFiles() 50 | { 51 | $directory = realpath($this->smarty->getCompileDir()); 52 | 53 | $di = new \RecursiveDirectoryIterator($directory); 54 | $it = new \RecursiveIteratorIterator($di, \RecursiveIteratorIterator::CHILD_FIRST); 55 | foreach ($it as $file) { 56 | $_file = $file->__toString(); 57 | 58 | if (preg_match("#[\\\\/]\.#", $_file)) { 59 | continue; 60 | } 61 | 62 | if ($file->isDir()) { 63 | rmdir($_file); 64 | } else { 65 | unlink($_file); 66 | } 67 | 68 | } 69 | } 70 | 71 | } 72 | 73 | 74 | class _object_toString 75 | { 76 | protected $string = null; 77 | public function __construct($string) { 78 | $this->string = (string) $string; 79 | } 80 | 81 | public function __toString() { 82 | return $this->string; 83 | } 84 | } 85 | 86 | class _object_noString 87 | { 88 | protected $string = null; 89 | public function __construct($string) { 90 | $this->string = (string) $string; 91 | } 92 | } 93 | 94 | ini_set('date.timezone', 'UTC'); 95 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | All contributions are welcome to this project. 4 | 5 | ## Contributor License Agreement 6 | 7 | Before a contribution can be merged into this project, please fill out the Contributor License Agreement (CLA) located at: 8 | 9 | http://opensource.box.com/cla 10 | 11 | To learn more about CLAs and why they are important to open source projects, please see the [Wikipedia entry](http://en.wikipedia.org/wiki/Contributor_License_Agreement). 12 | 13 | ## How to contribute 14 | 15 | * **File an issue** - if you found a bug, want to request an enhancement, or want to implement something (bug fix or feature). 16 | * **Send a pull request** - if you want to contribute code. Please be sure to file an issue first. 17 | 18 | ## Pull request best practices 19 | 20 | We want to accept your pull requests. Please follow these steps: 21 | 22 | ### Step 1: File an issue 23 | 24 | Before writing any code, please file an issue stating the problem you want to solve or the feature you want to implement. This allows us to give you feedback before you spend any time writing code. There may be a known limitation that can't be addressed, or a bug that has already been fixed in a different way. The issue allows us to communicate and figure out if it's worth your time to write a bunch of code for the project. 25 | 26 | ### Step 2: Fork this repository in GitHub 27 | 28 | This will create your own copy of our repository. 29 | 30 | ### Step 3: Add the upstream source 31 | 32 | The upstream source is the project under the Box organization on GitHub. To add an upstream source for this project, type: 33 | 34 | ``` 35 | git remote add upstream git@github.com:box/brainy.git 36 | ``` 37 | 38 | This will come in useful later. 39 | 40 | ### Step 4: Create a feature branch 41 | 42 | Create a branch with a descriptive name, such as `add-search`. 43 | 44 | ### Step 5: Push your feature branch to your fork 45 | 46 | As you develop code, continue to push code to your remote feature branch. Please make sure to include the issue number you're addressing in your commit message, such as: 47 | 48 | ``` 49 | git commit -m "Adding search (fixes #123)" 50 | ``` 51 | 52 | This helps us out by allowing us to track which issue your commit relates to. 53 | 54 | Keep a separate feature branch for each issue you want to address. 55 | 56 | ### Step 6: Rebase 57 | 58 | Before sending a pull request, rebase against upstream, such as: 59 | 60 | ``` 61 | git fetch upstream 62 | git rebase upstream/master 63 | ``` 64 | 65 | This will add your changes on top of what's already in upstream, minimizing merge issues. 66 | 67 | ### Step 7: Run the tests 68 | 69 | Make sure that all tests are passing before submitting a pull request. 70 | 71 | ### Step 8: Send the pull request 72 | 73 | Send the pull request from your feature branch to us. Be sure to include a description that lets us know what work you did. 74 | 75 | Keep in mind that we like to see one issue addressed per pull request, as this helps keep our git history clean and we can more easily track down issues. 76 | -------------------------------------------------------------------------------- /test/RegisterCompilerFunctionTest.php: -------------------------------------------------------------------------------- 1 | compilerFunction / unregister->compilerFunction methods 4 | * 5 | * @package PHPunit 6 | * @author Uwe Tews 7 | */ 8 | 9 | namespace Box\Brainy\Tests; 10 | 11 | 12 | class RegisterCompilerFunctionTest extends Smarty_TestCase 13 | { 14 | public static function myCompilerFunction($params, $smarty) 15 | { 16 | return "echo 'hello world {$params['var']}';\n"; 17 | } 18 | public static function myCompilerFunctionOpen($params, $smarty) 19 | { 20 | return "echo 'open tag';\n"; 21 | } 22 | public static function myCompilerFunctionClose($params, $smarty) 23 | { 24 | return "echo 'close tag';\n"; 25 | } 26 | 27 | /** 28 | * test register->compilerFunction method for function 29 | */ 30 | public function testRegisterCompilerFunction() { 31 | $this->smarty->registerPlugin( 32 | \Box\Brainy\Brainy::PLUGIN_COMPILER, 33 | 'testcompilerfunction', 34 | '\Box\Brainy\Tests\RegisterCompilerFunctionTest::myCompilerFunction' 35 | ); 36 | $this->assertEquals( 37 | '\Box\Brainy\Tests\RegisterCompilerFunctionTest::myCompilerFunction', 38 | $this->smarty->registered_plugins['compiler']['testcompilerfunction'] 39 | ); 40 | $this->assertEquals('hello world 1', $this->smarty->fetch('eval:{testcompilerfunction var=1}')); 41 | } 42 | 43 | /** 44 | * test register->compilerFunction method for blocks 45 | */ 46 | public function testRegisterCompilerFunctionBlock() { 47 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER, 'foo', '\Box\Brainy\Tests\RegisterCompilerFunctionTest::myCompilerFunctionOpen'); 48 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER, 'fooclose', '\Box\Brainy\Tests\RegisterCompilerFunctionTest::myCompilerFunctionClose'); 49 | $result = $this->smarty->fetch('eval:{foo} hallo {/foo}'); 50 | $this->assertEquals('open tag hallo close tag', $result); 51 | } 52 | /** 53 | * test unregister->compilerFunction method 54 | */ 55 | public function testUnregisterCompilerFunction() { 56 | $this->smarty->registerPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER, 'testcompilerfunction', '\Box\Brainy\Tests\RegisterCompilerFunctionTest::myCompilerFunction'); 57 | $this->smarty->unregisterPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER, 'testcompilerfunction'); 58 | $this->assertFalse(isset($this->smarty->registered_plugins[\Box\Brainy\Brainy::PLUGIN_COMPILER]['testcompilerfunction'])); 59 | } 60 | /** 61 | * test unregister->compilerFunction method not registered 62 | */ 63 | public function testUnregisterCompilerFunctionNotRegistered() { 64 | $this->smarty->unregisterPlugin(\Box\Brainy\Brainy::PLUGIN_COMPILER,'testcompilerfunction'); 65 | $this->assertFalse(isset($this->smarty->registered_plugins[\Box\Brainy\Brainy::PLUGIN_COMPILER]['testcompilerfunction'])); 66 | } 67 | } 68 | --------------------------------------------------------------------------------