pug-php / pug

Pug template engine for PHP
https://www.phug-lang.com
MIT License
391 stars 42 forks source link

Undefined variable: Math on line 0, offset 0 #206

Closed radiocity closed 6 years ago

radiocity commented 6 years ago

Hello, I encountered an issue with the following code:

mixin mymixin
  - var random = Math.round(Math.random() * (35535 - 10) + 10)
  h1 #{random}
+mymixin
$pug = new Pug([
  'cache' => false,
  'extension' => '.pug',
  'basedir' => DIR_PUG,
  'expressionLanguage' => 'js'
]);
echo $pug->renderFile('test');

I expected to get:

<h1>random number here</h1>

But I actually get:

ErrorException in mymixin:
Undefined variable: Math on line 0, offset 0

I couldnt use nodejs due to hosting restriction. Is it possible to enable Math functions? Thanks!

kylekatarnls commented 6 years ago

expressionLanguage js provide JS syntax but not JS engine, simply use PHP equivalent functions:

mixin mymixin
  - var random = mt_rand(10, 35535)
  h1 #{random}
+mymixin

PS: Using Math.round with Math.random does not give you equal probability, first and last number have twice less chances to output, prefer add 1, then floor in JS. In PHP mt_rand() already returns integers when you pass min and max arguments.

radiocity commented 6 years ago

Thank you, sir. Pug templates should work well with both php and js. Solution below:

mixin mymixin
  - var max = 255
  if Math
    - random = Math.floor(Math.random() * max)
  else
    - random = mt_rand(0, max);
  h1 #{random}
+mymixin

PS Don`t use single line conditional shorthand.

kylekatarnls commented 6 years ago

Hi, be careful, you miss the +1: mt_rand(0, max) is [0;max] (max included), when Math.floor(Math.random() * max) is [0;max[ (max excluded), use Math.floor(Math.random() * (max + 1)) to get equivalent results.

But if you need your PHP app to be compatible with templates used in JS, you should rather polyfill methods you use:

$pug->share('Math', [
  'floor' => 'floor',
  'random' => function () {
    return mt_rand() / mt_getrandmax();
  },
  // ceil, round, min, max, etc. if you need it.
]);

So you can use Math as if you were in JS:

mixin mymixin
  - var max = 255
  h1 #{Math.floor(Math.random() * max)}
+mymixin
kylekatarnls commented 6 years ago

Here is a more complete polyfill of Math for PHP (with es7 functions):

[
    'DEG_PER_RAD' => M_PI / 180,
    'E' => M_E,
    'LN2' => M_LN2,
    'LN10' => M_LN10,
    'LOG2E' => M_LOG2E,
    'LOG10E' => M_LOG10E,
    'PI' => M_PI,
    'RAD_PER_DEG' => 180 / M_PI,
    'SQRT1_2' => M_SQRT1_2,
    'SQRT2' => M_SQRT2,

    'abs' => 'abs',
    'acos' => 'acos',
    'acosh' => 'acosh',
    'asin' => 'asin',
    'asinh' => 'asinh',
    'atan' => 'atan',
    'atan2' => 'atan2',
    'atanh' => 'atanh',
    'cbrt' => function ($value) {
        return pow($value, 1 / 3);
    },
    'ceil' => 'ceil',
    'clamp' => function ($value, $min, $max) {
        return max($min, min($value, $max));
    },
    'clz32' => function ($value) {
        if ($value === -INF) {
            return 32;
        }
        if ($value < 0 || ($value |= 0) < 0) {
            return 0;
        }
        return 32 - ceil(log(1 + $value, 2));
    },
    'cos' => 'cos',
    'cosh' => 'cosh',
    'degrees' => 'rad2deg',
    'exp' => 'exp',
    'expm1' => 'expm1',
    'floor' => 'floor',
    'fround' => function ($value) {
        return unpack('f', pack('f', $value))[1];
    },
    'fscale' => function ($value, $inLow, $inHigh, $outLow, $outHigh) {
        if (in_array(NAN, [$value, $inLow, $inHigh, $outLow, $outHigh])) {
            return NAN;
        }
        return unpack('f', pack('f', (
            $value === INF || $value === -INF
                ? $value
                : (($value - $inLow) * ($inHigh - $outLow) / ($inHigh - $inLow) + $outLow)
        )))[1];
    },
    'hypot' => function ($value1, $value2) {
        return sqrt(array_sum(array_map(function ($number) {
            return $number * $number;
        }, func_get_args())));
    },
    'iaddh' => '',
    'imul' => function ($x, $y) {
        return ($x | 0) * ($y | 0) | 0;
    },
    'imulh' => function ($u, $v) {
        $urShift = function ($n, $s) {
            return ($n >= 0) ? ($n >> $s) :
                (($n & 0x7fffffff) >> $s) |
                (0x40000000 >> ($s - 1));
        };
        $UINT16 = 0xffff;
        $u = +$u;
        $v = +$v;
        $u0 = $u & $UINT16;
        $v0 = $v & $UINT16;
        $u1 = $u >> 16;
        $v1 = $v >> 16;
        $t = ($u1 * ($urShift($v0, 1) << 1)) + $urShift($u0 * $v0, 16);

        return $u1 * $v1 + ($t >> 16) + (($urShift($u0 * $v1, 1) << 1) + ($t & $UINT16) >> 16);
    },
    'isubh' => function ($x0, $x1, $y0, $y1) {
        $urShift = function ($n, $s) {
            return ($n >= 0) ? ($n >> $s) :
                (($n & 0x7fffffff) >> $s) |
                (0x40000000 >> ($s - 1));
        };
        $x0 = $urShift($x0, 1) << 1;
        $x1 = $urShift($x1, 1) << 1;
        $y0 = $urShift($y0, 1) << 1;

        return $x1 - ($urShift($y1, 1) << 1) - $urShift((~$x0 & $y0 | ~($x0 ^ $y0) & $x0 - ($urShift($y0, 1) << 1)), 31) | 0;
    },
    'log' => 'log',
    'log1p' => function ($value) {
        return log(1 + $value);
    },
    'log2' => function ($value) {
        return log($value, 2);
    },
    'log10' => 'log10',
    'max' => 'max',
    'min' => 'min',
    'pow' => 'pow',
    'radians' => 'deg2rad',
    'random' => function () {
        return mt_rand() / (mt_getrandmax() + 1);
    },
    'round' => 'round',
    'scale' => function ($value, $inLow, $inHigh, $outLow, $outHigh) {
        if (in_array(NAN, [$value, $inLow, $inHigh, $outLow, $outHigh])) {
            return NAN;
        }
        if ($value === INF || $value === -INF) {
            return $value;
        }
        return ($value - $inLow) * ($inHigh - $outLow) / ($inHigh - $inLow) + $outLow;
    },
    'sign' => function ($value) {
        return ($value > 0 ? 1 : ($value < 0 ? -1 : 0));
    },
    'signbit' => function ($value) {
        return $value >= 0;
    },
    'sin' => 'sin',
    'sinh' => 'sinh',
    'sqrt' => 'sqrt',
    'tan' => 'tan',
    'tanh' => 'tanh',
    'trunc' => function ($value) {
        return $value >= 0 ? floor($value) : ceil($value);
    },
    'umulh' => function ($u, $v) {
        $urShift = function ($n, $s) {
            return ($n >= 0) ? ($n >> $s) :
                (($n & 0x7fffffff) >> $s) |
                (0x40000000 >> ($s - 1));
        };
        $UINT16 = 0xffff;
        $u = +$u;
        $v = +$v;
        $u0 = $u & $UINT16;
        $v0 = $v & $UINT16;
        $u1 = $urShift($u, 16);
        $v1 = $urShift($v, 16);
        $t = ($u1 * ($urShift($v0, 1) << 1)) + $urShift($u0 * $v0, 16);

        return $u1 * $v1 + $urShift($t, 16) + $urShift(($urShift($u0 * $v1, 1) << 1) + ($t & $UINT16), 16);
    },
 ]

We will embed it by default in the next version of https://github.com/pug-php/js-phpize.

kylekatarnls commented 6 years ago

Fixed with jsphpize 2.4.0. You should be able to remove the share() statement, run composer update then get it working properly.