pug-php / pug

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

Passing arguments to helper function #100

Closed dadajuice closed 7 years ago

dadajuice commented 7 years ago

Hello,

I have a question and maybe an enhancement proposal concerning helper functions. I created a simple helper function which needs a number as an argument to do some formatting. I've included this function using the share method.

The problem I have is that when I pass a literal value it works perfectly, but when I want to pass a variable managed by the Pug template its not working as expected. Here's an example working as expected with literal value (200) :

each item in items
  li Example #{item.name} #{$format('money', 200)}

Here's an example which do not work using Pug variable.

I expected to get:

each item in items
  li Example #{item.name} #{$format('money', item.price)}

I get an error saying Use of undefined constant item which make perfect sense since the resulting PHP for the segment above is :

<?php foreach ($items as $item) { ?>
    <li>Example <?php echo \Jade\Compiler::getEscapedValue(\Jade\Compiler::getPropertyFromAnything($item, 'name'), '"') ?> <?php echo \Jade\Compiler::getEscapedValue($format('money', item.price), '"') ?>
    </li>
<?php } ?>

The item.price certainly doesn't exists in this context. I found a workaround knowing that what was passed to the function and its simply somehow direct PHP input.

each item in items
  :php $price = $item->getPrice();
  li Example #{item.name} #{$format('money', $price)}

My question : is this the desired way of using arguments with helper function and if there's a better way to to pass an argument?

I'd really love to remove as much php in my Pug as I can. I know my example code may not be the best use of a view since I could do the formatting before sending it to the Pug view, but I have some quick functions like this which can be very helpful when needing.

If there's currently no simpler way to access the Pug variables, I think it could be a nice addition to this phenomenal template engine.

Thanks!

kylekatarnls commented 7 years ago

Hi, it's not about the context, it's about js-to-php transformation. You cannot mix PHP and JS in the same expression. So you can write it all in PHP:

#{$format('money', $item->getPrice())}

Or with 'languageExpression' => 'js' option, I did not test but you can theorically do:

#{format('money', item.price)}

If you get trouble with the first syntax, it's a pug-php bug, with the second, it's probably an enhancement to implement in js-phpize. For mixed JS/PHP syntaxes, we won't fix it because the hybrid mode 'languageExpression' => 'auto' will disappear in the next version.

dadajuice commented 7 years ago

Thanks a lot for your prompt response! I've tested the second option with the expressionLanguage option and I still get some errors, but the generated PHP code seems to be more around what I'm looking for :)

each item in items
  li Example #{item.name} #{format('money', item.price)}

This shoots an error : Notice: Undefined index: __jpv_dot in pug.stream://data; The generated PHP code looks like :

<?php foreach ($items as $item) { ?>
        <li>Example <?php echo \Jade\Compiler::getEscapedValue(call_user_func($GLOBALS['__jpv_dot'], $item, 'name'), '"') ?> <?php echo \Jade\Compiler::getEscapedValue(function_exists('format') ? format('money', call_user_func($GLOBALS['__jpv_dot'], $item, 'price')) : call_user_func($format, 'money', call_user_func($GLOBALS['__jpv_dot'], $item, 'price')), '"') ?></li>
      <?php } ?>

Also, the item.name do not work anymore (triggers same error with the __jpv_dot index). If the $GLOBALS['__jpv_dot'] would be set I think it would do exactly what I want. For information, I've outputted the $GLOBALS variable and I didn't see anything related to Pug.

kylekatarnls commented 7 years ago

Indeed $GLOBALS['__jpv_dot'] is the helper from js-phpize to get a property. When you render a template, the helper declarations should be added first at the begenning of the rendered PHP.

Is this the complete render? Because I get an other error: when I test on the demo, format is not converted to $format, that's a tricky part because both could be valid in PHP. But you can bypass it by grouping your helpers in an array:

Try this template:

p Example #{item.name} #{helpers.format('money', item.price)}

With this data:

array(
  'item' => array(
    'name' => 'Foo',
    'price' => 12,
  ),
  'helpers' => array(
    'format' => function ($type, $price) {
      return $type . '-' . $price;
    },
  ),
)

The template compile well in js mode:

 <p>Example Foo money-12</p>

I hope this could fit your needs.

dadajuice commented 7 years ago

Sadly, I'm still getting the same error about $GLOBALS['__jpv_dot']. Here's the generated PHP (obtain in my cache folder).

<?php  ?> 
<p>Example <?php echo \Jade\Compiler::getEscapedValue(call_user_func($GLOBALS['__jpv_dot'], $item, 'name'), '"') ?> <?php echo \Jade\Compiler::getEscapedValue(function_exists('helpers') ? helpers('money', call_user_func($GLOBALS['__jpv_dot'], $item, 'price')) : call_user_func(call_user_func($GLOBALS['__jpv_dot'], $helpers, 'format'), 'money', call_user_func($GLOBALS['__jpv_dot'], $item, 'price')), '"') ?></p>

I see at the top of the generated PHP file an empty tag (<?php ?>). Is the global declarations supposed to go there normally ?

For information, here's how I construct the Pug instance :

$options = [
  'cache' => Configuration::getConfiguration('pug', 'cache'),
  'basedir' => ROOT_DIR . '/public',
  'prettyprint' => true, /* debug purpose */
  'expressionLanguage' => 'js'
];
$pug = new Pug($options);

$args = array(
  'item' => array(
    'name' => 'Foo',
    'price' => 12,
   ),
   'helpers' => array(
     'format' => function ($type, $price) {
       return $type . '-' . $price;
   })
);
$pug->render("p Example #{item.name} #{helpers.format('money', item.price)}", $args)

Also, I'm running on PHP 7.0.10 and I've included Pug to my project using composer. Here's the composer.lock segment if it can be of any help.

{
            "name": "pug-php/pug",
            "version": "2.5.0",
            "source": {
                "type": "git",
                "url": "https://github.com/pug-php/pug.git",
                "reference": "50c47a9f6446086cd8581596a53ab663bc936926"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/pug-php/pug/zipball/50c47a9f6446086cd8581596a53ab663bc936926",
                "reference": "50c47a9f6446086cd8581596a53ab663bc936926",
                "shasum": ""
            },
            "require": {
                "js-phpize/js-phpize": "^1.0",
                "php": ">=5.3.0"
            },
            "replace": {
                "kylekatarnls/jade-php": "self.version"
            },
            "require-dev": {
                "codeclimate/php-test-reporter": "dev-master",
                "phpunit/phpunit": ">=4.0"
            },
            "type": "library",
            "autoload": {
                "psr-0": {
                    "Pug\\": "src/",
                    "Jade\\": "src/"
                }
            },
            ...
}

Thanks again for your prompt response!

kylekatarnls commented 7 years ago

I confirm the bug with your code, I don't understand why the demo works.

kylekatarnls commented 7 years ago

I identified the bug, if js-phpize is called many times, the dependencies are replaced and not merge. Just release a js-phpize version, add a unit test for this issue, and you will be able to update and fix it.

kylekatarnls commented 7 years ago

Please upgrade to 2.5.1 and retry.

dadajuice commented 7 years ago

Just tested with 2.5.1 and I confirm that it works perfectly! Thank you very very much for your time. I'm taking the liberty to close this issue :)

kylekatarnls commented 7 years ago

Thanks to you for the report!