Authored by hf

do modify nginx.reg and test render template

  1 +<?php return function ($in, $debugopt = 1) {
  2 + $cx = array(
  3 + 'flags' => array(
  4 + 'jstrue' => false,
  5 + 'jsobj' => false,
  6 + 'spvar' => true,
  7 + 'prop' => false,
  8 + 'method' => false,
  9 + 'mustlok' => true,
  10 + 'echo' => false,
  11 + 'debug' => $debugopt,
  12 + ),
  13 + 'constants' => array(),
  14 + 'helpers' => array(),
  15 + 'blockhelpers' => array(),
  16 + 'hbhelpers' => array(),
  17 + 'partials' => array(),
  18 + 'scopes' => array(),
  19 + 'sp_vars' => array('root' => $in),
  20 + 'lcrun' => 'Plugin\LCRun3',
  21 +
  22 + );
  23 +
  24 + return '
  25 +This is a test '.htmlentities((string)Plugin\LCRun3::v($cx, $in, array('test')), ENT_QUOTES, 'UTF-8').'
  26 +
  27 +';
  28 +}
  29 +?>
  1 +<?php
  2 +namespace Plugin;
  3 +/**
  4 + * LightnCandy static class for compiled template runtime methods.
  5 + */
  6 +class LCRun3 {
  7 + const DEBUG_ERROR_LOG = 1;
  8 + const DEBUG_ERROR_EXCEPTION = 2;
  9 + const DEBUG_TAGS = 4;
  10 + const DEBUG_TAGS_ANSI = 12;
  11 + const DEBUG_TAGS_HTML = 20;
  12 +
  13 + /**
  14 + * LightnCandy runtime method for output debug info.
  15 + *
  16 + * @param string $v expression
  17 + * @param string $f runtime function name
  18 + * @param array<string,array|string|integer> $cx render time context
  19 + *
  20 + * @expect '{{123}}' when input '123', 'miss', array('flags' => array('debug' => LCRun3::DEBUG_TAGS), 'lcrun' => 'LCRun3'), ''
  21 + * @expect '<!--MISSED((-->{{#123}}<!--))--><!--SKIPPED--><!--MISSED((-->{{/123}}<!--))-->' when input '123', 'wi', array('flags' => array('debug' => LCRun3::DEBUG_TAGS_HTML), 'lcrun' => 'LCRun3'), false, false, function () {return 'A';}
  22 + */
  23 + public static function debug($v, $f, $cx) {
  24 + $params = array_slice(func_get_args(), 2);
  25 + $r = call_user_func_array((isset($cx['funcs'][$f]) ? $cx['funcs'][$f] : "{$cx['lcrun']}::$f"), $params);
  26 +
  27 + if ($cx['flags']['debug'] & self::DEBUG_TAGS) {
  28 + $ansi = $cx['flags']['debug'] & (self::DEBUG_TAGS_ANSI - self::DEBUG_TAGS);
  29 + $html = $cx['flags']['debug'] & (self::DEBUG_TAGS_HTML - self::DEBUG_TAGS);
  30 + $cs = ($html ? (($r !== '') ? '<!!--OK((-->' : '<!--MISSED((-->') : '')
  31 + . ($ansi ? (($r !== '') ? "\033[0;32m" : "\033[0;31m") : '');
  32 + $ce = ($html ? '<!--))-->' : '')
  33 + . ($ansi ? "\033[0m" : '');
  34 + switch ($f) {
  35 + case 'sec':
  36 + case 'ifv':
  37 + case 'unl':
  38 + case 'wi':
  39 + if ($r == '') {
  40 + if ($ansi) {
  41 + $r = "\033[0;33mSKIPPED\033[0m";
  42 + }
  43 + if ($html) {
  44 + $r = '<!--SKIPPED-->';
  45 + }
  46 + }
  47 + return "$cs{{#{$v}}}$ce{$r}$cs{{/{$v}}}$ce";
  48 + default:
  49 + return "$cs{{{$v}}}$ce";
  50 + }
  51 + } else {
  52 + return $r;
  53 + }
  54 + }
  55 +
  56 + /**
  57 + * LightnCandy runtime method for missing data error.
  58 + *
  59 + * @param array<string,array|string|integer> $cx render time context
  60 + * @param string $v expression
  61 + */
  62 + public static function miss($cx, $v) {
  63 + $e = "LCRun3: $v is not exist";
  64 + if ($cx['flags']['debug'] & self::DEBUG_ERROR_LOG) {
  65 + error_log($e);
  66 + return;
  67 + }
  68 + if ($cx['flags']['debug'] & self::DEBUG_ERROR_EXCEPTION) {
  69 + throw new Exception($e);
  70 + }
  71 + }
  72 +
  73 + /**
  74 + * LightnCandy runtime method for variable lookup. It is slower and only be used for instance property or method detection.
  75 + *
  76 + * @param array<string,array|string|integer> $cx render time context
  77 + * @param array<array|string|integer> $base current variable context
  78 + * @param array<string|integer> $path array of names for path
  79 + *
  80 + * @return null|string Return the value or null when not found
  81 + *
  82 + * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), 0, array('a', 'b')
  83 + * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0), 'mustlok' => 0), array('a' => array('b' => 3)), array('a', 'b')
  84 + * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), (Object) array('a' => array('b' => 3)), array('a', 'b')
  85 + * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 1, 'method' => 0, 'mustlok' => 0)), (Object) array('a' => array('b' => 3)), array('a', 'b')
  86 + */
  87 + public static function v($cx, $base, $path) {
  88 + $count = count($cx['scopes']);
  89 + while ($base) {
  90 + $v = $base;
  91 + foreach ($path as $name) {
  92 + if (is_array($v) && isset($v[$name])) {
  93 + $v = $v[$name];
  94 + continue;
  95 + }
  96 + if (is_object($v)) {
  97 + if ($cx['flags']['prop'] && isset($v->$name)) {
  98 + $v = $v->$name;
  99 + continue;
  100 + }
  101 + if ($cx['flags']['method'] && is_callable(array($v, $name))) {
  102 + $v = $v->$name();
  103 + continue;
  104 + }
  105 + }
  106 + if ($cx['flags']['mustlok']) {
  107 + unset($v);
  108 + break;
  109 + }
  110 + return null;
  111 + }
  112 + if (isset($v)) {
  113 + return $v;
  114 + }
  115 + $count--;
  116 + switch ($count) {
  117 + case -1:
  118 + $base = $cx['sp_vars']['root'];
  119 + break;
  120 + case -2:
  121 + return null;
  122 + default:
  123 + $base = $cx['scopes'][$count];
  124 + }
  125 + }
  126 + }
  127 +
  128 + /**
  129 + * LightnCandy runtime method for {{#if var}}.
  130 + *
  131 + * @param array<string,array|string|integer> $cx render time context
  132 + * @param array<array|string|integer>|string|integer|null $v value to be tested
  133 + *
  134 + * @return boolean Return true when the value is not null nor false.
  135 + *
  136 + * @expect false when input array(), null
  137 + * @expect false when input array(), 0
  138 + * @expect false when input array(), false
  139 + * @expect true when input array(), true
  140 + * @expect true when input array(), 1
  141 + * @expect false when input array(), ''
  142 + * @expect false when input array(), array()
  143 + * @expect true when input array(), array('')
  144 + * @expect true when input array(), array(0)
  145 + */
  146 + public static function ifvar($cx, $v) {
  147 + return !is_null($v) && ($v !== false) && ($v !== 0) && ($v !== 0.0) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true);
  148 + }
  149 +
  150 + /**
  151 + * LightnCandy runtime method for {{#if var}} when {{../var}} used.
  152 + *
  153 + * @param array<string,array|string|integer> $cx render time context
  154 + * @param array<array|string|integer>|string|integer|null $v value to be tested
  155 + * @param array<array|string|integer> $in input data with current scope
  156 + * @param Closure|null $truecb callback function when test result is true
  157 + * @param Closure|null $falsecb callback function when test result is false
  158 + *
  159 + * @return string The rendered string of the section
  160 + *
  161 + * @expect '' when input array('scopes' => array()), null, array(), null
  162 + * @expect '' when input array('scopes' => array()), null, array(), function () {return 'Y';}
  163 + * @expect 'Y' when input array('scopes' => array()), 1, array(), function () {return 'Y';}
  164 + * @expect 'N' when input array('scopes' => array()), null, array(), function () {return 'Y';}, function () {return 'N';}
  165 + */
  166 + public static function ifv($cx, $v, $in, $truecb, $falsecb = null) {
  167 + $ret = '';
  168 + if (self::ifvar($cx, $v)) {
  169 + if ($truecb) {
  170 + $cx['scopes'][] = $in;
  171 + $ret = $truecb($cx, $in);
  172 + array_pop($cx['scopes']);
  173 + }
  174 + } else {
  175 + if ($falsecb) {
  176 + $cx['scopes'][] = $in;
  177 + $ret = $falsecb($cx, $in);
  178 + array_pop($cx['scopes']);
  179 + }
  180 + }
  181 + return $ret;
  182 + }
  183 +
  184 + /**
  185 + * LightnCandy runtime method for {{#unless var}} when {{../var}} used.
  186 + *
  187 + * @param array<string,array|string|integer> $cx render time context
  188 + * @param array<array|string|integer>|string|integer|null $var value be tested
  189 + * @param array<array|string|integer>|string|integer|null $in input data with current scope
  190 + * @param Closure $truecb callback function when test result is true
  191 + * @param Closure|null $falsecb callback function when test result is false
  192 + *
  193 + * @return string Return rendered string when the value is not null nor false.
  194 + *
  195 + * @expect '' when input array('scopes' => array()), null, array(), null
  196 + * @expect 'Y' when input array('scopes' => array()), null, array(), function () {return 'Y';}
  197 + * @expect '' when input array('scopes' => array()), 1, array(), function () {return 'Y';}
  198 + * @expect 'Y' when input array('scopes' => array()), null, array(), function () {return 'Y';}, function () {return 'N';}
  199 + * @expect 'N' when input array('scopes' => array()), true, array(), function () {return 'Y';}, function () {return 'N';}
  200 + */
  201 + public static function unl($cx, $var, $in, $truecb, $falsecb = null) {
  202 + return self::ifv($cx, $var, $in, $falsecb, $truecb);
  203 + }
  204 +
  205 + /**
  206 + * LightnCandy runtime method for {{^var}} inverted section.
  207 + *
  208 + * @param array<string,array|string|integer> $cx render time context
  209 + * @param array<array|string|integer>|string|integer|null $v value to be tested
  210 + *
  211 + * @return boolean Return true when the value is not null nor false.
  212 + *
  213 + * @expect true when input array(), null
  214 + * @expect false when input array(), 0
  215 + * @expect true when input array(), false
  216 + * @expect false when input array(), 'false'
  217 + * @expect true when input array(), array()
  218 + * @expect false when input array(), array('1')
  219 + */
  220 + public static function isec($cx, $v) {
  221 + return is_null($v) || ($v === false) || (is_array($v) && (count($v) === 0));
  222 + }
  223 +
  224 + /**
  225 + * LightnCandy runtime method for {{{var}}} .
  226 + *
  227 + * @param array<string,array|string|integer> $cx render time context
  228 + * @param array<array|string|integer>|string|integer|null $v value to be output
  229 + *
  230 + * @return string The raw value of the specified variable
  231 + *
  232 + * @expect true when input array('flags' => array('jstrue' => 0)), true
  233 + * @expect 'true' when input array('flags' => array('jstrue' => 1)), true
  234 + * @expect '' when input array('flags' => array('jstrue' => 0)), false
  235 + * @expect 'false' when input array('flags' => array('jstrue' => 1)), false
  236 + * @expect 'false' when input array('flags' => array('jstrue' => 1)), false, true
  237 + * @expect 'Array' when input array('flags' => array('jstrue' => 1, 'jsobj' => 0)), array('a', 'b')
  238 + * @expect 'a,b' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'b')
  239 + * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'c' => 'b')
  240 + * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('c' => 'b')
  241 + * @expect 'a,true' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', true)
  242 + * @expect 'a,1' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1)), array('a',true)
  243 + * @expect 'a,' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1)), array('a',false)
  244 + * @expect 'a,false' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a',false)
  245 + */
  246 + public static function raw($cx, $v) {
  247 + if ($v === true) {
  248 + if ($cx['flags']['jstrue']) {
  249 + return 'true';
  250 + }
  251 + }
  252 +
  253 + if (($v === false)) {
  254 + if ($cx['flags']['jstrue']) {
  255 + return 'false';
  256 + }
  257 + }
  258 +
  259 + if (is_array($v)) {
  260 + if ($cx['flags']['jsobj']) {
  261 + if (count(array_diff_key($v, array_keys(array_keys($v)))) > 0) {
  262 + return '[object Object]';
  263 + } else {
  264 + $ret = array();
  265 + foreach ($v as $k => $vv) {
  266 + $ret[] = self::raw($cx, $vv);
  267 + }
  268 + return join(',', $ret);
  269 + }
  270 + } else {
  271 + return 'Array';
  272 + }
  273 + }
  274 +
  275 + return "$v";
  276 + }
  277 +
  278 + /**
  279 + * LightnCandy runtime method for {{var}} .
  280 + *
  281 + * @param array<string,array|string|integer> $cx render time context
  282 + * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
  283 + *
  284 + * @return string The htmlencoded value of the specified variable
  285 + *
  286 + * @expect 'a' when input array(), 'a'
  287 + * @expect 'a&amp;b' when input array(), 'a&b'
  288 + * @expect 'a&#039;b' when input array(), 'a\'b'
  289 + */
  290 + public static function enc($cx, $var) {
  291 + return htmlentities(self::raw($cx, $var), ENT_QUOTES, 'UTF-8');
  292 + }
  293 +
  294 + /**
  295 + * LightnCandy runtime method for {{var}} , and deal with single quote to same as handlebars.js .
  296 + *
  297 + * @param array<string,array|string|integer> $cx render time context
  298 + * @param array<array|string|integer>|string|integer|null $var value to be htmlencoded
  299 + *
  300 + * @return string The htmlencoded value of the specified variable
  301 + *
  302 + * @expect 'a' when input array(), 'a'
  303 + * @expect 'a&amp;b' when input array(), 'a&b'
  304 + * @expect 'a&#x27;b' when input array(), 'a\'b'
  305 + * @expect '&#x60;a&#x27;b' when input array(), '`a\'b'
  306 + */
  307 + public static function encq($cx, $var) {
  308 + return preg_replace('/`/', '&#x60;', preg_replace('/&#039;/', '&#x27;', htmlentities(self::raw($cx, $var), ENT_QUOTES, 'UTF-8')));
  309 + }
  310 +
  311 + /**
  312 + * LightnCandy runtime method for {{#var}} section.
  313 + *
  314 + * @param array<string,array|string|integer> $cx render time context
  315 + * @param array<array|string|integer>|string|integer|null $v value for the section
  316 + * @param array<array|string|integer>|string|integer|null $in input data with current scope
  317 + * @param boolean $each true when rendering #each
  318 + * @param Closure $cb callback function to render child context
  319 + * @param Closure|null $else callback function to render child context when {{else}}
  320 + *
  321 + * @return string The rendered string of the section
  322 + *
  323 + * @expect '' when input array('flags' => array('spvar' => 0)), false, false, false, function () {return 'A';}
  324 + * @expect '' when input array('flags' => array('spvar' => 0)), null, null, false, function () {return 'A';}
  325 + * @expect 'A' when input array('flags' => array('spvar' => 0)), true, true, false, function () {return 'A';}
  326 + * @expect 'A' when input array('flags' => array('spvar' => 0)), 0, 0, false, function () {return 'A';}
  327 + * @expect '-a=' when input array('flags' => array('spvar' => 0)), array('a'), array('a'), false, function ($c, $i) {return "-$i=";}
  328 + * @expect '-a=-b=' when input array('flags' => array('spvar' => 0)), array('a','b'), array('a','b'), false, function ($c, $i) {return "-$i=";}
  329 + * @expect '' when input array('flags' => array('spvar' => 0)), 'abc', 'abc', true, function ($c, $i) {return "-$i=";}
  330 + * @expect '-b=' when input array('flags' => array('spvar' => 0)), array('a' => 'b'), array('a' => 'b'), true, function ($c, $i) {return "-$i=";}
  331 + * @expect '1' when input array('flags' => array('spvar' => 0)), 'b', 'b', false, function ($c, $i) {return count($i);}
  332 + * @expect '1' when input array('flags' => array('spvar' => 0)), 1, 1, false, function ($c, $i) {return print_r($i, true);}
  333 + * @expect '0' when input array('flags' => array('spvar' => 0)), 0, 0, false, function ($c, $i) {return print_r($i, true);}
  334 + * @expect '{"b":"c"}' when input array('flags' => array('spvar' => 0)), array('b' => 'c'), array('b' => 'c'), false, function ($c, $i) {return json_encode($i);}
  335 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), array(), 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  336 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), array(), 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  337 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), false, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  338 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), false, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  339 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), '', 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  340 + * @expect 'cb' when input array('flags' => array('spvar' => 0)), '', 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  341 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), 0, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  342 + * @expect 'cb' when input array('flags' => array('spvar' => 0)), 0, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  343 + * @expect 'inv' when input array('flags' => array('spvar' => 0)), new stdClass, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  344 + * @expect 'cb' when input array('flags' => array('spvar' => 0)), new stdClass, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';}
  345 + * @expect '268' when input array('flags' => array('spvar' => 1), 'sp_vars'=>array('root' => 0)), array(1,3,4), 0, false, function ($c, $i) {return $i * 2;}
  346 + * @expect '038' when input array('flags' => array('spvar' => 1), 'sp_vars'=>array('root' => 0)), array(1,3,'a'=>4), 0, true, function ($c, $i) {return $i * $c['sp_vars']['index'];}
  347 + */
  348 + public static function sec($cx, $v, $in, $each, $cb, $else = null) {
  349 + $isAry = is_array($v) || ($v instanceof ArrayObject);
  350 + $isTrav = $v instanceof Traversable;
  351 + $loop = $each;
  352 + $keys = null;
  353 + $last = null;
  354 + $isObj = false;
  355 +
  356 + if ($isAry && $else !== null && count($v) === 0) {
  357 + $cx['scopes'][] = $in;
  358 + $ret = $else($cx, $in);
  359 + array_pop($cx['scopes']);
  360 + return $ret;
  361 + }
  362 +
  363 + // #var, detect input type is object or not
  364 + if (!$loop && $isAry) {
  365 + $keys = array_keys($v);
  366 + $loop = (count(array_diff_key($v, array_keys($keys))) == 0);
  367 + $isObj = !$loop;
  368 + }
  369 +
  370 + if (($loop && $isAry) || $isTrav) {
  371 + if ($each && !$isTrav) {
  372 + // Detect input type is object or not when never done once
  373 + if ($keys == null) {
  374 + $keys = array_keys($v);
  375 + $isObj = (count(array_diff_key($v, array_keys($keys))) > 0);
  376 + }
  377 + }
  378 + $ret = array();
  379 + $cx['scopes'][] = $in;
  380 + $i = 0;
  381 + if ($cx['flags']['spvar']) {
  382 + $old_spvar = $cx['sp_vars'];
  383 + $cx['sp_vars'] = array(
  384 + '_parent' => $old_spvar,
  385 + 'root' => $old_spvar['root'],
  386 + );
  387 + if (!$isTrav) {
  388 + $last = count($keys) - 1;
  389 + }
  390 + }
  391 + foreach ($v as $index => $raw) {
  392 + if ($cx['flags']['spvar']) {
  393 + $cx['sp_vars']['first'] = ($i === 0);
  394 + $cx['sp_vars']['last'] = ($i == $last);
  395 + $cx['sp_vars']['key'] = $index;
  396 + $cx['sp_vars']['index'] = $i;
  397 + $i++;
  398 + }
  399 + $ret[] = $cb($cx, $raw);
  400 + }
  401 + if ($cx['flags']['spvar']) {
  402 + if ($isObj) {
  403 + unset($cx['sp_vars']['key']);
  404 + } else {
  405 + unset($cx['sp_vars']['last']);
  406 + }
  407 + unset($cx['sp_vars']['index']);
  408 + unset($cx['sp_vars']['first']);
  409 + $cx['sp_vars'] = $old_spvar;
  410 + }
  411 + array_pop($cx['scopes']);
  412 + return join('', $ret);
  413 + }
  414 + if ($each) {
  415 + if ($else !== null) {
  416 + $cx['scopes'][] = $in;
  417 + $ret = $else($cx, $v);
  418 + array_pop($cx['scopes']);
  419 + return $ret;
  420 + }
  421 + return '';
  422 + }
  423 + if ($isAry) {
  424 + $cx['scopes'][] = $in;
  425 + $ret = $cb($cx, $v);
  426 + array_pop($cx['scopes']);
  427 + return $ret;
  428 + }
  429 +
  430 + if ($v === true) {
  431 + return $cb($cx, $in);
  432 + }
  433 +
  434 + if (!is_null($v) && ($v !== false)) {
  435 + return $cb($cx, $v);
  436 + }
  437 +
  438 + if ($else !== null) {
  439 + $cx['scopes'][] = $in;
  440 + $ret = $else($cx, $in);
  441 + array_pop($cx['scopes']);
  442 + return $ret;
  443 + }
  444 +
  445 + return '';
  446 + }
  447 +
  448 + /**
  449 + * LightnCandy runtime method for {{#with var}} .
  450 + *
  451 + * @param array<string,array|string|integer> $cx render time context
  452 + * @param array<array|string|integer>|string|integer|null $v value to be the new context
  453 + * @param array<array|string|integer>|string|integer|null $in input data with current scope
  454 + * @param Closure $cb callback function to render child context
  455 + * @param Closure|null $else callback function to render child context when {{else}}
  456 + *
  457 + * @return string The rendered string of the token
  458 + *
  459 + * @expect '' when input array(), false, false, function () {return 'A';}
  460 + * @expect '' when input array(), null, null, function () {return 'A';}
  461 + * @expect '{"a":"b"}' when input array(), array('a'=>'b'), array('a'=>'c'), function ($c, $i) {return json_encode($i);}
  462 + * @expect '-b=' when input array(), 'b', array('a'=>'b'), function ($c, $i) {return "-$i=";}
  463 + */
  464 + public static function wi($cx, $v, $in, $cb, $else = null) {
  465 + if (($v === false) || ($v === null)) {
  466 + return $else ? $else($cx, $in) : '';
  467 + }
  468 + $cx['scopes'][] = $in;
  469 + $ret = $cb($cx, $v);
  470 + array_pop($cx['scopes']);
  471 + return $ret;
  472 + }
  473 +
  474 + /**
  475 + * LightnCandy runtime method for {{> partial}} .
  476 + *
  477 + * @param array<string,array|string|integer> $cx render time context
  478 + * @param string $p partial name
  479 + * @param array<array|string|integer>|string|integer|null $v value to be the new context
  480 + *
  481 + * @return string The rendered string of the partial
  482 + *
  483 + */
  484 + public static function p($cx, $p, $v, $sp = '') {
  485 + $param = $v[0][0];
  486 +
  487 + if (is_array($v[1])) {
  488 + if (is_array($v[0][0])) {
  489 + $param = array_merge($v[0][0], $v[1]);
  490 + } else if (($cx['flags']['method'] || $cx['flags']['prop']) && is_object($v[0][0])) {
  491 + foreach ($v[1] as $i => $v) {
  492 + $param->$i = $v;
  493 + }
  494 + }
  495 + }
  496 +
  497 + return call_user_func($cx['partials'][$p], $cx, $param, $sp);
  498 + }
  499 +
  500 + /**
  501 + * LightnCandy runtime method for custom helpers.
  502 + *
  503 + * @param array<string,array|string|integer> $cx render time context
  504 + * @param string $ch the name of custom helper to be executed
  505 + * @param array<array> $vars variables for the helper
  506 + * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
  507 + *
  508 + * @return string The rendered string of the token
  509 + *
  510 + * @expect '=-=' when input array('helpers' => array('a' => function ($i) {return "=$i[0]=";})), 'a', array(array('-'),array()), 'raw'
  511 + * @expect '=&amp;=' when input array('helpers' => array('a' => function ($i) {return "=$i[0]=";})), 'a', array(array('&'),array()), 'enc'
  512 + * @expect '=&#x27;=' when input array('helpers' => array('a' => function ($i) {return "=$i[0]=";})), 'a', array(array('\''),array()), 'encq'
  513 + * @expect '=b=' when input array('helpers' => array('a' => function ($i,$j) {return "={$j['a']}=";})), 'a', array(array(),array('a' => 'b')), 'raw'
  514 + */
  515 + public static function ch($cx, $ch, $vars, $op) {
  516 + return self::chret(call_user_func_array($cx['helpers'][$ch], $vars), $op);
  517 + }
  518 +
  519 + /**
  520 + * LightnCandy runtime method to handle response of custom helpers.
  521 + *
  522 + * @param string|array<string,array|string|integer> $ret return value from custom helper
  523 + * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
  524 + *
  525 + * @return string The rendered string of the token
  526 + *
  527 + * @expect '=&=' when input '=&=', 'raw'
  528 + * @expect '=&amp;&#039;=' when input '=&\'=', 'enc'
  529 + * @expect '=&amp;&#x27;=' when input '=&\'=', 'encq'
  530 + * @expect '=&amp;&#039;=' when input array('=&\'='), 'enc'
  531 + * @expect '=&amp;&#x27;=' when input array('=&\'='), 'encq'
  532 + * @expect '=&amp;=' when input array('=&=', false), 'enc'
  533 + * @expect '=&=' when input array('=&=', false), 'raw'
  534 + * @expect '=&=' when input array('=&=', 'raw'), 'enc'
  535 + * @expect '=&amp;&#x27;=' when input array('=&\'=', 'encq'), 'raw'
  536 + */
  537 + public static function chret($ret, $op) {
  538 + if (is_array($ret)) {
  539 + if (isset($ret[1]) && $ret[1]) {
  540 + $op = $ret[1];
  541 + }
  542 + $ret = $ret[0];
  543 + }
  544 +
  545 + switch ($op) {
  546 + case 'enc':
  547 + return htmlentities($ret, ENT_QUOTES, 'UTF-8');
  548 + case 'encq':
  549 + return preg_replace('/&#039;/', '&#x27;', htmlentities($ret, ENT_QUOTES, 'UTF-8'));
  550 + }
  551 + return $ret;
  552 + }
  553 +
  554 + /**
  555 + * LightnCandy runtime method for Handlebars.js style custom helpers.
  556 + *
  557 + * @param array<string,array|string|integer> $cx render time context
  558 + * @param string $ch the name of custom helper to be executed
  559 + * @param array<array|string|integer>|string|integer|null $vars variables for the helper
  560 + * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'.
  561 + * @param boolean $inverted the logic will be inverted
  562 + * @param Closure|null $cb callback function to render child context
  563 + * @param Closure|null $else callback function to render child context when {{else}}
  564 + *
  565 + * @return string The rendered string of the token
  566 + */
  567 + public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = null, $else = null) {
  568 + $isBlock = (is_object($cb) && ($cb instanceof Closure));
  569 + $args = $vars[0];
  570 + $options = array(
  571 + 'name' => $ch,
  572 + 'hash' => $vars[1],
  573 + '_this' => $isBlock ? $op : $inverted,
  574 + );
  575 +
  576 + // $invert the logic
  577 + if ($inverted) {
  578 + $tmp = $else;
  579 + $else = $cb;
  580 + $cb = $tmp;
  581 + }
  582 +
  583 + if ($isBlock) {
  584 + $options['fn'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $cb) {
  585 + if ($cx['flags']['echo']) {
  586 + ob_start();
  587 + }
  588 + if ($context === '_NO_INPUT_HERE_') {
  589 + $cx['scopes'][] = $op;
  590 + $ret = $cb($cx, $op);
  591 + } else {
  592 + $cx['scopes'][] = $op;
  593 + $ret = $cb($cx, $context);
  594 + }
  595 + array_pop($cx['scopes']);
  596 + return $cx['flags']['echo'] ? ob_get_clean() : $ret;
  597 + };
  598 + }
  599 +
  600 + if ($else) {
  601 + $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) {
  602 + if ($cx['flags']['echo']) {
  603 + ob_start();
  604 + }
  605 + if ($context === '_NO_INPUT_HERE_') {
  606 + $ret = $else($cx, $op);
  607 + } else {
  608 + $cx['scopes'][] = $op;
  609 + $ret = $else($cx, $context);
  610 + array_pop($cx['scopes']);
  611 + }
  612 + return $cx['flags']['echo'] ? ob_get_clean() : $ret;
  613 + };
  614 + }
  615 +
  616 + // prepare $options['data']
  617 + if ($cx['flags']['spvar']) {
  618 + $options['data'] = $cx['sp_vars'];
  619 + }
  620 +
  621 + $args[] = $options;
  622 + $e = null;
  623 + $r = true;
  624 +
  625 + try {
  626 + $r = call_user_func_array($cx['hbhelpers'][$ch], $args);
  627 + } catch (Exception $E) {
  628 + $e = "LCRun3: call custom helper '$ch' error: " . $E->getMessage();
  629 + }
  630 +
  631 + if($e !== null) {
  632 + if ($cx['flags']['debug'] & self::DEBUG_ERROR_LOG) {
  633 + error_log($e);
  634 + }
  635 + if ($cx['flags']['debug'] & self::DEBUG_ERROR_EXCEPTION) {
  636 + throw new Exception($e);
  637 + }
  638 + }
  639 +
  640 + return self::chret($r, $isBlock ? 'raw' : $op);
  641 + }
  642 +
  643 + /**
  644 + * LightnCandy runtime method for block custom helpers.
  645 + *
  646 + * @param array<string,array|string|integer> $cx render time context
  647 + * @param string $ch the name of custom helper to be executed
  648 + * @param array<array|string|integer>|string|integer|null $vars variables for the helper
  649 + * @param array<array|string|integer>|string|integer|null $in input data with current scope
  650 + * @param boolean $inverted the logic will be inverted
  651 + * @param Closure $cb callback function to render child context
  652 + * @param Closure|null $else callback function to render child context when {{else}}
  653 + *
  654 + * @return string The rendered string of the token
  655 + *
  656 + * @expect '4.2.3' when input array('blockhelpers' => array('a' => function ($cx) {return array($cx,2,3);})), 'a', array(0, 0), 4, false, function($cx, $i) {return implode('.', $i);}
  657 + * @expect '2.6.5' when input array('blockhelpers' => array('a' => function ($cx,$in) {return array($cx,$in[0],5);})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
  658 + * @expect '' when input array('blockhelpers' => array('a' => function ($cx,$in) {})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);}
  659 + */
  660 + public static function bch($cx, $ch, $vars, $in, $inverted, $cb, $else = null) {
  661 + $r = call_user_func($cx['blockhelpers'][$ch], $in, $vars[0], $vars[1]);
  662 +
  663 + // $invert the logic
  664 + if ($inverted) {
  665 + $tmp = $else;
  666 + $else = $cb;
  667 + $cb = $tmp;
  668 + }
  669 +
  670 + $ret = '';
  671 + if (is_null($r)) {
  672 + if ($else) {
  673 + $cx['scopes'][] = $in;
  674 + $ret = $else($cx, $r);
  675 + array_pop($cx['scopes']);
  676 + }
  677 + } else {
  678 + if ($cb) {
  679 + $cx['scopes'][] = $in;
  680 + $ret = $cb($cx, $r);
  681 + array_pop($cx['scopes']);
  682 + }
  683 + }
  684 +
  685 + return $ret;
  686 + }
  687 +}
@@ -72,9 +72,9 @@ class TemplateLayout implements View_Interface @@ -72,9 +72,9 @@ class TemplateLayout implements View_Interface
72 { 72 {
73 $request = Dispatcher::getInstance()->getRequest(); 73 $request = Dispatcher::getInstance()->getRequest();
74 $config = Application::app()->getConfig()->get('application'); 74 $config = Application::app()->getConfig()->get('application');
75 - $tplExt = '.' . $config->view->ext;  
76 - $viewPath = $this->getScriptPath() . '/' . $request->module;  
77 - $viewName = $viewPath . '/' . $request->controller . '/' . $tpl . $tplExt; 75 + $tplExt = $config->template->ext;
  76 + $viewPath = $config->template->path;
  77 + $viewName = $viewPath . '/' . $request->module . '/' . $request->controller . '/' . $tpl . '.' . $tplExt;
78 // 判断视图模板文件是否存在, 不存在则直接返回空 78 // 判断视图模板文件是否存在, 不存在则直接返回空
79 if (!file_exists($viewName)) { 79 if (!file_exists($viewName)) {
80 return ''; 80 return '';
@@ -97,12 +97,14 @@ class TemplateLayout implements View_Interface @@ -97,12 +97,14 @@ class TemplateLayout implements View_Interface
97 } 97 }
98 // 第一次渲染该模板的流程:取得模板内容 => 预编译成PHP函数 => 写入服务器生成PHP文件 98 // 第一次渲染该模板的流程:取得模板内容 => 预编译成PHP函数 => 写入服务器生成PHP文件
99 else { 99 else {
  100 + echo $config->template->partials;
100 $template = file_get_contents($viewName, false, null); 101 $template = file_get_contents($viewName, false, null);
101 $phpStr = LightnCandy::compile($template, array( 102 $phpStr = LightnCandy::compile($template, array(
102 // DEBUG: LightnCandy::FLAG_RENDER_DEBUG | LightnCandy::FLAG_ERROR_EXCEPTION 103 // DEBUG: LightnCandy::FLAG_RENDER_DEBUG | LightnCandy::FLAG_ERROR_EXCEPTION
103 'flags' => LightnCandy::FLAG_MUSTACHE | LightnCandy::FLAG_HANDLEBARS, // 使用MUSTACHE和HANDLEBARS的模板格式 104 'flags' => LightnCandy::FLAG_MUSTACHE | LightnCandy::FLAG_HANDLEBARS, // 使用MUSTACHE和HANDLEBARS的模板格式
104 - 'basedir' => array($viewPath . '/partials'), // 模板里使用 {{> partial_name}} 时查找的目录 105 + 'basedir' => array($config->template->partials), // 模板里使用 {{> partial_name}} 时查找的目录
105 'fileext' => array($tplExt), // 允许查找文件的后缀 106 'fileext' => array($tplExt), // 允许查找文件的后缀
  107 + 'lcrun' => 'Plugin\LCRun3', // 指定编译模板的runtime
106 )); 108 ));
107 // 文件流方式读取PHP方法 109 // 文件流方式读取PHP方法
108 $renderer = LightnCandy::prepare($phpStr); 110 $renderer = LightnCandy::prepare($phpStr);
@@ -112,7 +114,7 @@ class TemplateLayout implements View_Interface @@ -112,7 +114,7 @@ class TemplateLayout implements View_Interface
112 114
113 // 装载内容,调用PHP函数 115 // 装载内容,调用PHP函数
114 try { 116 try {
115 - $result = $renderer($this->_tpl_vars); 117 + $result = $renderer($tpl_vars);
116 } catch (Exception $e) { 118 } catch (Exception $e) {
117 $result = ''; 119 $result = '';
118 } 120 }
@@ -129,7 +131,7 @@ class TemplateLayout implements View_Interface @@ -129,7 +131,7 @@ class TemplateLayout implements View_Interface
129 public function setScriptPath($path) 131 public function setScriptPath($path)
130 { 132 {
131 $result = false; 133 $result = false;
132 - if (is_readable($path)) { 134 + if (is_dir($path)) {
133 $this->tpl_dir = $path; 135 $this->tpl_dir = $path;
134 $result = true; 136 $result = true;
135 } 137 }
1 Windows Registry Editor Version 5.00 1 Windows Registry Editor Version 5.00
2 2
3 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NGINX\Parameters] 3 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NGINX\Parameters]
4 -"Application"="E:\\nginx\\nginx.exe" 4 +"Application"="D:\\workspace\\yohobuy.git.dev.yoho.cn\\script\\nginx\\nginx.exe"
5 "AppParameters"="" 5 "AppParameters"=""
6 -"AppDirectory"="E:\\nginx\\"  
  6 +"AppDirectory"="D:\\workspace\\"
@@ -5,7 +5,7 @@ E: @@ -5,7 +5,7 @@ E:
5 cd nginx 5 cd nginx
6 nginx.exe 6 nginx.exe
7 7
8 -@echo off  
9 -echo Starting PHP FastCGI...  
10 -RunHiddenConsole.exe D:\php\php-cgi.exe -b 127.0.0.1:9000 -c D:\php\php.ini 8 +#@echo off
  9 +#echo Starting PHP FastCGI...
  10 +#RunHiddenConsole.exe D:\php\php-cgi.exe -b 127.0.0.1:9000 -c D:\php\php.ini
11 EXIT 11 EXIT
  1 +该目录用于存放前端相关的模板
  1 +{{>layout/header}}
  2 +
  3 +This is a test {{test}}
  4 +
  5 +{{>layout/footer}}
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <meta charset="utf-8">
  5 + <title>{{title}}</title>
  6 + <meta name="keywords" content="{{keywords}}">
  7 + <meta name="description" content="{{description}}">
  8 + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
  9 + <meta http-equiv="cleartype" content="on">
  10 + <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  11 + <meta content="telephone=no" name="format-detection" />
  12 + <meta content="email=no" name="format-detection" />
  13 +</head>
  14 +<body>
  1 +该目录用于存放前端相关的模板
@@ -76,16 +76,15 @@ class Bootstrap extends Bootstrap_Abstract @@ -76,16 +76,15 @@ class Bootstrap extends Bootstrap_Abstract
76 */ 76 */
77 public function _initLayout(Dispatcher $dispatcher) 77 public function _initLayout(Dispatcher $dispatcher)
78 { 78 {
  79 + // 关闭自动渲染模板
  80 + $dispatcher->autoRender(false);
  81 +
79 // 判断到不是AJAX请求时, 使用自定义的模板渲染 (Mustache or Handlebars) 82 // 判断到不是AJAX请求时, 使用自定义的模板渲染 (Mustache or Handlebars)
80 if (!$dispatcher->getRequest()->isXmlHttpRequest()) { 83 if (!$dispatcher->getRequest()->isXmlHttpRequest()) {
81 $layout = new TemplateLayout(); 84 $layout = new TemplateLayout();
82 - $layout->setScriptPath($this->_config->application->layout->path); 85 + $layout->setScriptPath($this->_config->application->template->path);
83 $dispatcher->setView($layout); 86 $dispatcher->setView($layout);
84 } 87 }
85 - // 关闭自动渲染模板  
86 - else {  
87 - $dispatcher->autoRender(false);  
88 - }  
89 } 88 }
90 89
91 /** 90 /**
@@ -7,6 +7,7 @@ class IndexController extends AbstractAction @@ -7,6 +7,7 @@ class IndexController extends AbstractAction
7 { 7 {
8 public function indexAction() 8 public function indexAction()
9 { 9 {
10 - echo 'hello world'; 10 + $this->_view->assign('title', 'YOHO!有货');
  11 + $this->_view->display('index', array('test' => 'hello world'));
11 } 12 }
12 } 13 }
@@ -4,7 +4,7 @@ application.directory = APPLICATION_PATH "/application" @@ -4,7 +4,7 @@ application.directory = APPLICATION_PATH "/application"
4 ;;website library 4 ;;website library
5 application.library = ROOT_PATH "/library" 5 application.library = ROOT_PATH "/library"
6 ;;默认模块 6 ;;默认模块
7 -application.modules = "index,default" 7 +application.modules = "Index"
8 ;;加载 8 ;;加载
9 application.bootstrap = APPLICATION_PATH "/application/Bootstrap.php" 9 application.bootstrap = APPLICATION_PATH "/application/Bootstrap.php"
10 ;;view文件的扩展名 10 ;;view文件的扩展名
@@ -19,7 +19,7 @@ application.dispatcher.defaultController = "index" @@ -19,7 +19,7 @@ application.dispatcher.defaultController = "index"
19 application.dispatcher.defaultAction = "index" 19 application.dispatcher.defaultAction = "index"
20 20
21 ;;初始化命名空间 21 ;;初始化命名空间
22 -application.namespaces = "Action,Plugin" 22 +application.namespaces = "Action,Configs,Plugin"
23 23
24 ;;使用composer 24 ;;使用composer
25 composer.autoload = 0 25 composer.autoload = 0
@@ -42,3 +42,6 @@ application.dispatcher.catchException = True @@ -42,3 +42,6 @@ application.dispatcher.catchException = True
42 42
43 ;模板预编译目录,该目录需要有读写权限 43 ;模板预编译目录,该目录需要有读写权限
44 application.template.compile = ROOT_PATH "/compile/m.yohobuy.com" 44 application.template.compile = ROOT_PATH "/compile/m.yohobuy.com"
  45 +application.template.path = ROOT_PATH "/template/m.yohobuy.com/actions"
  46 +application.template.partials = ROOT_PATH "/template/m.yohobuy.com/partials"
  47 +application.template.ext = "phtml"
@@ -4,7 +4,7 @@ application.directory = APPLICATION_PATH "/application" @@ -4,7 +4,7 @@ application.directory = APPLICATION_PATH "/application"
4 ;;website library 4 ;;website library
5 application.library = ROOT_PATH "/library" 5 application.library = ROOT_PATH "/library"
6 ;;默认模块 6 ;;默认模块
7 -application.modules = "Default,Test" 7 +application.modules = "Index"
8 ;;加载 8 ;;加载
9 application.bootstrap = APPLICATION_PATH "/application/Bootstrap.php" 9 application.bootstrap = APPLICATION_PATH "/application/Bootstrap.php"
10 ;;view文件的扩展名 10 ;;view文件的扩展名
@@ -42,3 +42,6 @@ application.dispatcher.catchException = False @@ -42,3 +42,6 @@ application.dispatcher.catchException = False
42 42
43 ;模板预编译目录,该目录需要有读写权限 43 ;模板预编译目录,该目录需要有读写权限
44 application.template.compile = ROOT_PATH "/compile/m.yohobuy.com" 44 application.template.compile = ROOT_PATH "/compile/m.yohobuy.com"
  45 +application.template.path = ROOT_PATH "/template/m.yohobuy.com/actions"
  46 +application.template.partials = ROOT_PATH "/template/m.yohobuy.com/partials"
  47 +application.template.ext = "phtml"
@@ -4,7 +4,7 @@ application.directory = APPLICATION_PATH "/application" @@ -4,7 +4,7 @@ application.directory = APPLICATION_PATH "/application"
4 ;;website library 4 ;;website library
5 application.library = ROOT_PATH "/library" 5 application.library = ROOT_PATH "/library"
6 ;;默认模块 6 ;;默认模块
7 -application.modules = "Default,Test" 7 +application.modules = "Index"
8 ;;加载 8 ;;加载
9 application.bootstrap = APPLICATION_PATH "/application/Bootstrap.php" 9 application.bootstrap = APPLICATION_PATH "/application/Bootstrap.php"
10 ;;view文件的扩展名 10 ;;view文件的扩展名
@@ -42,3 +42,6 @@ application.dispatcher.catchException = True @@ -42,3 +42,6 @@ application.dispatcher.catchException = True
42 42
43 ;模板预编译目录,该目录需要有读写权限 43 ;模板预编译目录,该目录需要有读写权限
44 application.template.compile = ROOT_PATH "/compile/m.yohobuy.com" 44 application.template.compile = ROOT_PATH "/compile/m.yohobuy.com"
  45 +application.template.path = ROOT_PATH "/template/m.yohobuy.com/actions"
  46 +application.template.partials = ROOT_PATH "/template/m.yohobuy.com/partials"
  47 +application.template.ext = "phtml"