Get a working render function by a string of PHP code. This method may requires php setting allow_url_include=1 and allow_url_fopen=1 , or access right to tmp file system.
public static prepare ( string $php, string | null $tmpDir = null, boolean $delete = true ) : Closure | false | ||
$php | string | PHP code |
$tmpDir | string | null | Optional, change temp directory for php include file saved by prepare() when cannot include PHP code with data:// format. |
$delete | boolean | Optional, delete temp php file when set to tru. Default is true, set it to false for debug propose |
return | Closure | false | result of include() |
/** * @dataProvider issueProvider */ public function testIssues($issue) { global $tmpdir; $php = LightnCandy::compile($issue['template'], isset($issue['options']) ? $issue['options'] : null); $context = LightnCandy::getContext(); $parsed = print_r(LightnCandy::$lastParsed, true); if (count($context['error'])) { $this->fail('Compile failed due to:' . print_r($context['error'], true) . "\nPARSED: {$parsed}"); } $renderer = LightnCandy::prepare($php); $this->assertEquals($issue['expected'], $renderer($issue['data'], array('debug' => $issue['debug'])), "PHP CODE:\n{$php}\n{$parsed}"); }
/** * @dataProvider renderErrorProvider */ public function testRenderingErrorLog($test) { start_catch_error_log(); $php = LightnCandy::compile($test['template'], $test['options']); $renderer = LightnCandy::prepare($php); $renderer(null, array('debug' => Runtime::DEBUG_ERROR_LOG)); $e = stop_catch_error_log(); if ($e) { $this->assertEquals(array($test['expected']), $e); } else { $this->markTestIncomplete('skip HHVM'); } }
public function renderWidget($widget) { $filename = get_class($widget) . '.hbs'; $filename = str_replace('\\', '/', $filename); $filename = $this->themePath . '/' . $filename; if (!file_exists($filename)) { throw new RuntimeException("Can't find widget template: " . $filename); } $template = file_get_contents($filename); $helper = new Helper(); $phpStr = LightnCandy::compile($template, ['flags' => LightnCandy::FLAG_INSTANCE | LightnCandy::FLAG_METHOD, 'helpers' => ['route' => 'Materia\\Helper::route']]); $renderer = LightnCandy::prepare($phpStr); $data = ['widget' => $widget]; $data['urlGenerator'] = $this->urlGenerator; $data['materia'] = $this; return $renderer($data); }
/** * @dataProvider jsonSpecProvider */ public function testSpecs($spec) { global $tmpdir; $flag = LightnCandy::FLAG_MUSTACHE | LightnCandy::FLAG_ERROR_EXCEPTION; if ($spec['name'] == 'Interpolation - Expansion' || $spec['name'] == 'Interpolation - Alternate Delimiters' || $spec['desc'] == 'Lambdas used for sections should receive the raw section string.' || $spec['name'] == 'Section - Expansion' || $spec['name'] == 'Section - Alternate Delimiters' || $spec['name'] == 'Section - Multiple Calls' || $spec['name'] == 'Inverted Section') { $this->markTestIncomplete('Not supported case: complex mustache lambdas'); } if (isset($spec['data']['lambda']['php'])) { $spec['data']['lambda'] = getFunctionCode('function ($text = null) {' . $spec['data']['lambda']['php'] . '}'); } foreach (array($flag, $flag | LightnCandy::FLAG_STANDALONEPHP) as $f) { global $calls; $calls = 0; $php = LightnCandy::compile($spec['template'], array('flags' => $f, 'partials' => isset($spec['partials']) ? $spec['partials'] : null, 'basedir' => $tmpdir)); $parsed = print_r(LightnCandy::$lastParsed, true); $renderer = LightnCandy::prepare($php); $this->assertEquals($spec['expected'], $renderer($spec['data'], array('debug' => 0)), "SPEC:\n" . print_r($spec, true) . "\nPHP CODE: {$php}\nPARSED: {$parsed}"); } }
/** * @dataProvider jsonSpecProvider */ public function testSpecs($spec) { global $tmpdir; global $tested; global $test_flags; recursive_unset($spec, '!sparsearray'); recursive_lambda_fix($spec['data']); if (isset($spec['options']['data'])) { recursive_lambda_fix($spec['options']['data']); } //// Skip bad specs // 1. No expected nor exception in spec if (!isset($spec['expected']) && !isset($spec['exception'])) { $this->markTestIncomplete("Skip [{$spec['file']}#{$spec['description']}]#{$spec['no']} , no expected result in spec, skip."); } // 2. Not supported case: foo/bar path if ($spec['it'] === 'literal paths' && $spec['no'] === 58 || $spec['it'] === 'literal paths' && $spec['no'] === 59 || $spec['it'] === 'this keyword nested inside path' || $spec['it'] === 'this keyword nested inside helpers param' || $spec['it'] === 'should handle invalid paths' || $spec['it'] === 'parameter data throws when using complex scope references' || $spec['it'] === 'block with complex lookup using nested context') { $this->markTestIncomplete('Not supported case: foo/bar path'); } // 3. Different API, no need to test if ($spec['it'] === 'registering undefined partial throws an exception') { $this->markTestIncomplete('Not supported case: just skip it'); } // 4. block parameters, special case now do not support if ($spec['it'] === 'should not take presedence over pathed values') { $this->markTestIncomplete('Not supported case: just skip it'); } // 5. Not supported case: helperMissing and blockHelperMissing if ($spec['it'] === 'if a context is not found, helperMissing is used' || $spec['it'] === 'if a context is not found, custom helperMissing is used' || $spec['it'] === 'if a value is not found, custom helperMissing is used' || $spec['it'] === 'should include in simple block calls' || $spec['it'] === 'should include full id' || $spec['it'] === 'should include full id if a hash is passed' || $spec['it'] === 'lambdas resolved by blockHelperMissing are bound to the context') { $this->markTestIncomplete('Not supported case: just skip it'); } // 6. Not supported case: misc if ($spec['description'] === 'compat mode' || $spec['description'] === 'directives' || $spec['file'] === 'specs/handlebars/spec/track-ids.json' || $spec['it'] === 'knows how to report the correct line number in errors' || $spec['it'] === 'knows how to report the correct line number in errors when the first character is a newline' || $spec['it'] === 'should allow block params on chained helpers' || $spec['it'] === 'chained inverted sections' || $spec['description'] === 'decorators' || $spec['it'] === 'helper for raw block gets parameters' || $spec['template'] === '{{foo}') { $this->markTestIncomplete('Not supported case: just skip it'); } // TODO: require fix if ($spec['template'] === '{{#with .}}{{#*inline "myPartial"}}success{{/inline}}{{/with}}{{> myPartial}}' || $spec['it'] === 'functions returning safestrings shouldn\'t be escaped' || $spec['it'] === 'rendering function partial in vm mode' || $spec['it'] === 'provides each nested helper invocation its own options hash' || $spec['template'] === '{{echo (header)}}' || $spec['it'] === 'block functions without context argument' || $spec['it'] === 'depthed block functions with context argument' || $spec['it'] === 'block functions with context argument') { $this->markTestIncomplete('TODO: require fix'); } // FIX SPEC if ($spec['it'] === 'should take presednece over parent block params') { $spec['helpers']['goodbyes']['php'] = 'function($options) { static $value; if($value === null) { $value = 1; } return $options->fn(array("value" => "bar"), array("blockParams" => ($options["fn.blockParams"] === 1) ? array($value++, $value++) : null));}'; } if ($spec['it'] === 'should handle undefined and null' && $spec['expected'] === 'true true object') { $spec['expected'] = 'true true array'; } foreach ($test_flags as $f) { // setup helpers $tested++; $helpers = array(); $helpersList = ''; foreach (array_merge(isset($spec['globalHelpers']) && is_array($spec['globalHelpers']) ? $spec['globalHelpers'] : array(), isset($spec['helpers']) && is_array($spec['helpers']) ? $spec['helpers'] : array()) as $name => $func) { if (!isset($func['php'])) { $this->markTestIncomplete("Skip [{$spec['file']}#{$spec['description']}]#{$spec['no']} , no PHP helper code provided for this case."); } $hname = preg_replace('/\\.|\\//', '_', "custom_helper_{$spec['no']}_{$tested}_{$name}"); $helpers[$name] = $hname; $helper = preg_replace('/\\$options->(\\w+)/', '$options[\'$1\']', patch_this(preg_replace('/\\$block\\/\\*\\[\'(.+?)\'\\]\\*\\/->(.+?)\\(/', '$block[\'$2\'](', patch_safestring(preg_replace('/function/', "function {$hname}", $func['php'], 1))))); if ($spec['it'] === 'helper block with complex lookup expression' && $name === 'goodbyes') { $helper = preg_replace('/\\[\'fn\'\\]\\(\\)/', '[\'fn\'](array())', $helper); } $helpersList .= "{$helper}\n"; eval($helper); } try { $partials = isset($spec['globalPartials']) ? $spec['globalPartials'] : array(); // Do not use array_merge() here because it destories numeric key if (isset($spec['partials'])) { foreach ($spec['partials'] as $k => $v) { $partials[$k] = $v; } } if (isset($spec['compileOptions']['preventIndent'])) { if ($spec['compileOptions']['preventIndent']) { $f = $f | LightnCandy::FLAG_PREVENTINDENT; } } if (isset($spec['compileOptions']['explicitPartialContext'])) { if ($spec['compileOptions']['explicitPartialContext']) { $f = $f | LightnCandy::FLAG_PARTIALNEWCONTEXT; } } if (isset($spec['compileOptions']['ignoreStandalone'])) { if ($spec['compileOptions']['ignoreStandalone']) { $f = $f | LightnCandy::FLAG_IGNORESTANDALONE; } } if (isset($spec['compileOptions']['stringParams'])) { if ($spec['compileOptions']['stringParams']) { $f = $f | LightnCandy::FLAG_STRINGPARAMS; } } if (isset($spec['compileOptions']['knownHelpersOnly'])) { if ($spec['compileOptions']['knownHelpersOnly']) { $f = $f | LightnCandy::FLAG_KNOWNHELPERSONLY; } } $php = LightnCandy::compile($spec['template'], array('flags' => $f, 'hbhelpers' => $helpers, 'basedir' => $tmpdir, 'partials' => $partials)); $parsed = print_r(LightnCandy::$lastParsed, true); } catch (Exception $e) { // Exception as expected, pass! if (isset($spec['exception'])) { continue; } // Failed this case $this->fail('Exception:' . $e->getMessage()); } $renderer = LightnCandy::prepare($php); if ($spec['description'] === 'Tokenizer') { // no compile error means passed continue; } try { $ropt = array('debug' => Runtime::DEBUG_ERROR_EXCEPTION); if (isset($spec['options']['data'])) { $ropt['data'] = $spec['options']['data']; } $result = $renderer($spec['data'], $ropt); } catch (Exception $e) { if (!isset($spec['expected'])) { // expected error and catched here, so passed continue; } $this->fail("Rendering Error in {$spec['file']}#{$spec['description']}]#{$spec['no']}:{$spec['it']} PHP CODE: {$php}\nPARSED: {$parsed}\n" . $e->getMessage()); } if (!isset($spec['expected'])) { $this->fail('Should Fail:' . print_r($spec, true) . "PHP CODE: {$php}\nPARSED: {$parsed}\nHELPERS:{$helpersList}"); } $this->assertEquals($spec['expected'], $result, "[{$spec['file']}#{$spec['description']}]#{$spec['no']}:{$spec['it']} PHP CODE: {$php}\nPARSED: {$parsed}\nHELPERS:{$helpersList}"); } }
<?php use LightnCandy\LightnCandy; $template = '{{> (partial_name_helper type)}}'; $data = array('type' => 'dog', 'name' => 'Lucky', 'age' => 5); function partial_name_helper($type) { switch ($type[0]) { case 'man': case 'woman': return 'people'; case 'dog': case 'cat': return 'animal'; default: return 'default'; } } $php = LightnCandy::compile($template, array('flags' => LightnCandy::FLAG_HANDLEBARSJS | LightnCandy::FLAG_RUNTIMEPARTIAL | LightnCandy::FLAG_ERROR_EXCEPTION, 'helpers' => array('partial_name_helper'), 'partials' => array('people' => 'This is {{name}}, he is {{age}} years old.', 'animal' => 'This is {{name}}, it is {{age}} years old.', 'default' => 'This is {{name}}.'))); $renderer = LightnCandy::prepare($php); echo "Data:\n"; print_r($data); echo "\nTemplate:\n{$template}\n"; echo "\nCode:\n{$php}\n\n"; echo "\nOutput:\n"; echo $renderer($data); echo "\n";
/** * Fetch rendered template * * @param string $template Template pathname relative to templates directory * @param array $data Associative array of template variables * * @return string */ public function fetch($template, $data = []) { $data = array_merge($this->defaultVariables, $data); $php = Engine::compile($this->getTemplate($template), array('flags' => Engine::FLAG_ERROR_EXCEPTION | Engine::FLAG_ERROR_LOG | Engine::FLAG_INSTANCE | Engine::FLAG_MUSTACHE | Engine::FLAG_HANDLEBARS, 'basedir' => $this->directories, 'fileext' => $this->extensions, 'helpers' => $this->helpers, 'hbhelpers' => $this->block_helpers)); $renderer = Engine::prepare($php); return $renderer(array_merge($data ?: array()), LCRun3::DEBUG_ERROR_LOG); }