Jump to content

Recommended Posts

Posted

Invoices are not sent. The error message is as follows:

 

Oh noes!

Unclose tag, expecting endfor on line 98 in /home/facilpla/public_html/admin/vendors/h2o/h2o/parser.php

 

Has anyone had the same problem? How to fix it?

Posted

Facilplan

 

I had the same issue but I do not remember if it was my fault or the email template (Unpaid Invoice). 

 

An invoice has been created for your account and is attached to this email in PDF format.
{% for invoice in invoices %}
Invoice #: {invoice.id_code}{% endfor %}

 

Make sure that the {% endfor %} is there

 

Regards

Posted

Not sure what does your system status say? Does it have any warnings?

System status say: Unclose tag, expecting endfor on line 98 in /home/facilpla/public_html/admin/vendors/h2o/h2o/parser.php

 

Send invoices is the only thing that does not work. The welcome email and the creation of the service, it works.

Posted

Try using the code from mine, I don't have that issue.

 

<?php
class H2o_Lexer {
    function __construct($options = array()) {
        $this->options = $options;

        $trim = '';
        if ($this->options['TRIM_TAGS'])
            $trim = '(?:\r?\n)?';

        $this->pattern = ('/\G(.*?)(?:' .
            preg_quote($this->options['BLOCK_START']). '(.*?)' .preg_quote($this->options['BLOCK_END']) . $trim . '|' .
            preg_quote($this->options['VARIABLE_START']). '(.*?)' .preg_quote($this->options['VARIABLE_END']) . '|' .
            preg_quote($this->options['COMMENT_START']). '(.*?)' .preg_quote($this->options['COMMENT_END']) . $trim . ')/sm'
        );
    }

    function tokenize($source) {
        $result = new TokenStream;
        $pos = 0;
        $matches = array();
        preg_match_all($this->pattern, $source, $matches, PREG_SET_ORDER);

        foreach ($matches as $match) {
            if ($match[1])
            $result->feed('text', $match[1], $pos);
            $tagpos = $pos + strlen($match[1]);
            if ($match[2])
            $result->feed('block', trim($match[2]), $tagpos);
            elseif ($match[3])
            $result->feed('variable', trim($match[3]), $tagpos);
            elseif ($match[4])
            $result->feed('comment', trim($match[4]), $tagpos);
            $pos += strlen($match[0]);
        }
        if ($pos < strlen($source)){
            $result->feed('text', substr($source, $pos), $pos);
        }
        $result->close();
        return $result;
    }
}

class H2o_Parser {
    var $first;
    var $storage = array();
    var $filename;
    var $runtime;
    
    function __construct($source, $filename, $runtime, $options) {
        $this->options = $options;
        //$this->source = $source;
        $this->runtime = $runtime;
        $this->filename = $filename;
        $this->first = true;
        
        $this->lexer = new H2o_Lexer($options);
        $this->tokenstream = $this->lexer->tokenize($source);
        $this->storage = array(
          'blocks' => array(),
          'templates' => array(),
          'included' => array()
        );
    }

    function &parse() {
        $until = func_get_args();
        $nodelist = new NodeList($this);
        while($token = $this->tokenstream->next()) { 
            //$token = $this->tokenstream->current();
            switch($token->type) {
                case 'text' :
                    $node = new TextNode($token->content, $token->position);
                    break;
                case 'variable' :
                    $args = H2o_Parser::parseArguments($token->content, $token->position);
                    $variable = array_shift($args);
                    $filters = $args;
                    $node = new VariableNode($variable, $filters, $token->position);
                    break;
                case 'comment' :
                    $node = new CommentNode($token->content);
                    break;
                case 'block' :
                    if (in_array($token->content, $until)) {
                        $this->token = $token;                      
                        return $nodelist;
                    }
                    @list($name, $args) = preg_split('/\s+/',$token->content, 2);
                    $node = H2o::createTag($name, $args, $this, $token->position);
                    $this->token = $token;
            }
            $this->searching = join(',',$until);
            $this->first = false;
            $nodelist->append($node);
        }

        if ($until) {
            throw new TemplateSyntaxError('Unclose tag, expecting '. $until[0]);
        }
        return $nodelist;
    }

    function skipTo($until) {
        $this->parse($until);
        return null;
    }

    # Parse arguments
    static function parseArguments($source = null, $fpos = 0){
        $parser = new ArgumentLexer($source, $fpos);
        $result = array();
        $current_buffer = &$result;
        $filter_buffer = array();
        $tokens = $parser->parse();
        foreach ($tokens as $token) {
            list($token, $data) = $token;
            if ($token == 'filter_start') {
                $filter_buffer = array();
                $current_buffer = &$filter_buffer;
            }
            elseif ($token == 'filter_end') {
                if (count($filter_buffer)) {
	
                    $i = count($result)-1;
                    if ( is_array($result[$i]) ) $result[$i]['filters'][] = $filter_buffer;
                    else $result[$i] = array(0 => $result[$i], 'filters' => array($filter_buffer));
                }
                $current_buffer = &$result;
            }
            elseif ($token == 'boolean') {
                $current_buffer[] = ($data === 'true'? true : false);
            }            
            elseif ($token == 'name') {
                $current_buffer[] = symbol($data);
            }
            elseif ($token == 'number' || $token == 'string') { 
                $current_buffer[] = $data;
            } 
            elseif ($token == 'named_argument') {
                $last = $current_buffer[count($current_buffer) - 1];
                if (!is_array($last))
                    $current_buffer[] = array();

                $namedArgs =& $current_buffer[count($current_buffer) - 1]; 
                list($name,$value) = array_map('trim', explode(':', $data, 2));
                
                # if argument value is variable mark it
                $value = self::parseArguments($value);
                $namedArgs[$name] = $value[0];
            }
            elseif( $token == 'operator') {
                $current_buffer[] = array('operator'=>$data);
            }
        }
        return $result;
    }
}

class H2O_RE {
    static $whitespace, $seperator, $parentheses, $pipe, $filter_end, $operator, $boolean, $number,  $string, $i18n_string, $name, $named_args;

    static function init() {
        $r = 'strip_regex';
        
        self::$whitespace   = '/\s+/m';
        self::$parentheses  = '/\(|\)/m';
        self::$filter_end   = '/;/';
        self::$boolean    = '/true|false/';
        self::$seperator    = '/,/';
        self::$pipe         = '/\|/';
        self::$operator     = '/\s?(>|<|>=|<=|!=|==|!|and |not |or )\s?/i';
        self::$number       = '/\d+(\.\d*)?/';
        self::$name         = '/[a-zA-Z_][a-zA-Z0-9-_]*(?:\.[a-zA-Z_0-9][a-zA-Z0-9_-]*)*/';
        
        self::$string       = '/(?:
                "([^"\\\\]*(?:\\\\.[^"\\\\]*)*)" |   # Double Quote string   
                \'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\' # Single Quote String
        )/xsm';
        self::$i18n_string  = "/_\({$r(self::$string)}\) | {$r(self::$string)}/xsm";

        self::$named_args   = "{
            ({$r(self::$name)})(?:{$r(self::$whitespace)})?
            : 
            (?:{$r(self::$whitespace)})?({$r(self::$i18n_string)}|{$r(self::$number)}|{$r(self::$name)})
        }x";
    }
}
H2O_RE::init();

class ArgumentLexer {
    private $source;
    private $match;
    private $pos = 0, $fpos, $eos;
    private $operator_map = array(
        '!' => 'not', '!='=> 'ne', '==' => 'eq', '>' => 'gt', '<' => 'lt', '<=' => 'le', '>=' => 'ge'
    );

    function __construct($source, $fpos = 0){
        if (!is_null($source))
          $this->source = $source;
        $this->fpos=$fpos;
    }

    function parse(){
        $result = array();
        $filtering = false;
        while (!$this->eos()) {
            $this->scan(H2O_RE::$whitespace);
            if (!$filtering) {
                if ($this->scan(H2O_RE::$operator)){
                    $operator = trim($this->match);
                    if(isset($this->operator_map[$operator]))
                        $operator = $this->operator_map[$operator];
                    $result[] = array('operator', $operator);
                }
                elseif ($this->scan(H2O_RE::$boolean))
                    $result[] = array('boolean', $this->match);
                elseif ($this->scan(H2O_RE::$named_args))
                    $result[] = array('named_argument', $this->match);                      
                elseif ($this->scan(H2O_RE::$name))
                    $result[] = array('name', $this->match);
                elseif ($this->scan(H2O_RE::$pipe)) {
                    $filtering = true;
                    $result[] = array('filter_start', $this->match);
                }
                elseif ($this->scan(H2O_RE::$seperator))
                    $result[] = array('separator', null);
                elseif ($this->scan(H2O_RE::$i18n_string))
                    $result[] = array('string', $this->match);
                elseif ($this->scan(H2O_RE::$number))
                    $result[] = array('number', $this->match);
                else
                    throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition());
            } 
            else {
                // parse filters, with chaining and ";" as filter end character
                if ($this->scan(H2O_RE::$pipe)) {
                    $result[] = array('filter_end', null);
                    $result[] = array('filter_start', null);
                }
                elseif ($this->scan(H2O_RE::$seperator))
                    $result[] = array('separator', null);
                elseif ($this->scan(H2O_RE::$filter_end)) {
                    $result[] = array('filter_end', null);
                    $filtering = false;
                }
                elseif ($this->scan(H2O_RE::$boolean))
                    $result[] = array('boolean', $this->match);
                elseif ($this->scan(H2O_RE::$named_args))
                    $result[] = array('named_argument', $this->match);
                elseif ($this->scan(H2O_RE::$name))
                    $result[] = array('name', $this->match);
                elseif ($this->scan(H2O_RE::$i18n_string))
                    $result[] = array('string', $this->match);
                elseif ($this->scan(H2O_RE::$number))
                    $result[] = array('number', $this->match);          
                else
                    throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition());
            }
        }
        // if we are still in the filter state, we add a filter_end token.
        if ($filtering)
            $result[] = array('filter_end', null);
        return $result;
    }

    # String scanner
    function scan($regexp) {
        if (preg_match($regexp . 'A', $this->source, $match, null, $this->pos)) {
            $this->match = $match[0];
            $this->pos += strlen($this->match);
            return true;
        }
        return false;
    }

    function eos() {
        return $this->pos >= strlen($this->source);
    }
    
    /**
     * return the position in the template
     */
    function getPosition() {
        return $this->fpos + $this->pos;
    }
}
?>
Posted

Facilplan

 

I had the same issue but I do not remember if it was my fault or the email template (Unpaid Invoice). 

 

An invoice has been created for your account and is attached to this email in PDF format.

{% for invoice in invoices %}

Invoice #: {invoice.id_code}{% endfor %}

 

Make sure that the {% endfor %} is there

 

Regards

Thank you Sanecty for your answer, but I think that I don't explained properly.

When I create an invoice manually, then I can "see" "edit" "pay" options, but the Status is "Unsent". When I click the "send" button the system tells me:

Oh noes!

Unclose tag, expecting endfor on line 98 in /home/facilpla/public_html/admin/vendors/h2o/h2o/parser.php

Printing Stack Trace:

#0 /home/facilpla/public_html/admin/vendors/h2o/h2o/tags.php(112): H2o_Parser->parse('endfor', 'else')

#1 /home/facilpla/public_html/admin/vendors/h2o/h2o.php(151): For_Tag->__construct('invoice in invo...', Object(H2o_Parser), 111)

#2 /home/facilpla/public_html/admin/vendors/h2o/h2o/parser.php(89): H2o::createTag('for', 'invoice in invo...', Object(H2o_Parser), 111)

#3 /home/facilpla/public_html/admin/vendors/h2o/h2o.php(106): H2o_Parser->parse()

#4 /home/facilpla/public_html/admin/vendors/h2o/h2o.php(142): H2o->parse('Hola {contact.f...')

#5 /home/facilpla/public_html/admin/app/models/emails.php(753): H2o::parseString('Hola {contact.f...', Array)

#6 /home/facilpla/public_html/admin/app/models/emails.php(479): Emails->buildEmail('invoice_deliver...', '1', 'es_es', Array)

#7 /home/facilpla/public_html/admin/components/invoice_delivery/invoice_delivery.php(122): Emails->send('invoice_deliver...', '1', 'es_es', 'contacto@mundod...', Array, NULL, NULL, Array, Array)

#8 /home/facilpla/public_html/admin/app/controllers/admin_clients.php(294): InvoiceDelivery->deliverInvoices(Array, 'email', 'contacto@mundod...', '1', Array)

#9 /home/facilpla/public_html/admin/lib/dispatcher.php(111): AdminClients->invoices()

#10 /home/facilpla/public_html/admin/index.php(21): Dispatcher::dispatch('/admin/clients/...')

#11 {main}

 

This problem only happens in "send invoice" because the welcome message arrives correctly.

Posted

Try using the code from mine, I don't have that issue.

 

<?php
class H2o_Lexer {
    function __construct($options = array()) {
        $this->options = $options;

        $trim = '';
        if ($this->options['TRIM_TAGS'])
            $trim = '(?:\r?\n)?';

        $this->pattern = ('/\G(.*?)(?:' .
            preg_quote($this->options['BLOCK_START']). '(.*?)' .preg_quote($this->options['BLOCK_END']) . $trim . '|' .
            preg_quote($this->options['VARIABLE_START']). '(.*?)' .preg_quote($this->options['VARIABLE_END']) . '|' .
            preg_quote($this->options['COMMENT_START']). '(.*?)' .preg_quote($this->options['COMMENT_END']) . $trim . ')/sm'
        );
    }

    function tokenize($source) {
        $result = new TokenStream;
        $pos = 0;
        $matches = array();
        preg_match_all($this->pattern, $source, $matches, PREG_SET_ORDER);

        foreach ($matches as $match) {
            if ($match[1])
            $result->feed('text', $match[1], $pos);
            $tagpos = $pos + strlen($match[1]);
            if ($match[2])
            $result->feed('block', trim($match[2]), $tagpos);
            elseif ($match[3])
            $result->feed('variable', trim($match[3]), $tagpos);
            elseif ($match[4])
            $result->feed('comment', trim($match[4]), $tagpos);
            $pos += strlen($match[0]);
        }
        if ($pos < strlen($source)){
            $result->feed('text', substr($source, $pos), $pos);
        }
        $result->close();
        return $result;
    }
}

class H2o_Parser {
    var $first;
    var $storage = array();
    var $filename;
    var $runtime;
    
    function __construct($source, $filename, $runtime, $options) {
        $this->options = $options;
        //$this->source = $source;
        $this->runtime = $runtime;
        $this->filename = $filename;
        $this->first = true;
        
        $this->lexer = new H2o_Lexer($options);
        $this->tokenstream = $this->lexer->tokenize($source);
        $this->storage = array(
          'blocks' => array(),
          'templates' => array(),
          'included' => array()
        );
    }

    function &parse() {
        $until = func_get_args();
        $nodelist = new NodeList($this);
        while($token = $this->tokenstream->next()) { 
            //$token = $this->tokenstream->current();
            switch($token->type) {
                case 'text' :
                    $node = new TextNode($token->content, $token->position);
                    break;
                case 'variable' :
                    $args = H2o_Parser::parseArguments($token->content, $token->position);
                    $variable = array_shift($args);
                    $filters = $args;
                    $node = new VariableNode($variable, $filters, $token->position);
                    break;
                case 'comment' :
                    $node = new CommentNode($token->content);
                    break;
                case 'block' :
                    if (in_array($token->content, $until)) {
                        $this->token = $token;                      
                        return $nodelist;
                    }
                    @list($name, $args) = preg_split('/\s+/',$token->content, 2);
                    $node = H2o::createTag($name, $args, $this, $token->position);
                    $this->token = $token;
            }
            $this->searching = join(',',$until);
            $this->first = false;
            $nodelist->append($node);
        }

        if ($until) {
            throw new TemplateSyntaxError('Unclose tag, expecting '. $until[0]);
        }
        return $nodelist;
    }

    function skipTo($until) {
        $this->parse($until);
        return null;
    }

    # Parse arguments
    static function parseArguments($source = null, $fpos = 0){
        $parser = new ArgumentLexer($source, $fpos);
        $result = array();
        $current_buffer = &$result;
        $filter_buffer = array();
        $tokens = $parser->parse();
        foreach ($tokens as $token) {
            list($token, $data) = $token;
            if ($token == 'filter_start') {
                $filter_buffer = array();
                $current_buffer = &$filter_buffer;
            }
            elseif ($token == 'filter_end') {
                if (count($filter_buffer)) {
	
                    $i = count($result)-1;
                    if ( is_array($result[$i]) ) $result[$i]['filters'][] = $filter_buffer;
                    else $result[$i] = array(0 => $result[$i], 'filters' => array($filter_buffer));
                }
                $current_buffer = &$result;
            }
            elseif ($token == 'boolean') {
                $current_buffer[] = ($data === 'true'? true : false);
            }            
            elseif ($token == 'name') {
                $current_buffer[] = symbol($data);
            }
            elseif ($token == 'number' || $token == 'string') { 
                $current_buffer[] = $data;
            } 
            elseif ($token == 'named_argument') {
                $last = $current_buffer[count($current_buffer) - 1];
                if (!is_array($last))
                    $current_buffer[] = array();

                $namedArgs =& $current_buffer[count($current_buffer) - 1]; 
                list($name,$value) = array_map('trim', explode(':', $data, 2));
                
                # if argument value is variable mark it
                $value = self::parseArguments($value);
                $namedArgs[$name] = $value[0];
            }
            elseif( $token == 'operator') {
                $current_buffer[] = array('operator'=>$data);
            }
        }
        return $result;
    }
}

class H2O_RE {
    static $whitespace, $seperator, $parentheses, $pipe, $filter_end, $operator, $boolean, $number,  $string, $i18n_string, $name, $named_args;

    static function init() {
        $r = 'strip_regex';
        
        self::$whitespace   = '/\s+/m';
        self::$parentheses  = '/\(|\)/m';
        self::$filter_end   = '/;/';
        self::$boolean    = '/true|false/';
        self::$seperator    = '/,/';
        self::$pipe         = '/\|/';
        self::$operator     = '/\s?(>|<|>=|<=|!=|==|!|and |not |or )\s?/i';
        self::$number       = '/\d+(\.\d*)?/';
        self::$name         = '/[a-zA-Z_][a-zA-Z0-9-_]*(?:\.[a-zA-Z_0-9][a-zA-Z0-9_-]*)*/';
        
        self::$string       = '/(?:
                "([^"\\\\]*(?:\\\\.[^"\\\\]*)*)" |   # Double Quote string   
                \'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\' # Single Quote String
        )/xsm';
        self::$i18n_string  = "/_\({$r(self::$string)}\) | {$r(self::$string)}/xsm";

        self::$named_args   = "{
            ({$r(self::$name)})(?:{$r(self::$whitespace)})?
            : 
            (?:{$r(self::$whitespace)})?({$r(self::$i18n_string)}|{$r(self::$number)}|{$r(self::$name)})
        }x";
    }
}
H2O_RE::init();

class ArgumentLexer {
    private $source;
    private $match;
    private $pos = 0, $fpos, $eos;
    private $operator_map = array(
        '!' => 'not', '!='=> 'ne', '==' => 'eq', '>' => 'gt', '<' => 'lt', '<=' => 'le', '>=' => 'ge'
    );

    function __construct($source, $fpos = 0){
        if (!is_null($source))
          $this->source = $source;
        $this->fpos=$fpos;
    }

    function parse(){
        $result = array();
        $filtering = false;
        while (!$this->eos()) {
            $this->scan(H2O_RE::$whitespace);
            if (!$filtering) {
                if ($this->scan(H2O_RE::$operator)){
                    $operator = trim($this->match);
                    if(isset($this->operator_map[$operator]))
                        $operator = $this->operator_map[$operator];
                    $result[] = array('operator', $operator);
                }
                elseif ($this->scan(H2O_RE::$boolean))
                    $result[] = array('boolean', $this->match);
                elseif ($this->scan(H2O_RE::$named_args))
                    $result[] = array('named_argument', $this->match);                      
                elseif ($this->scan(H2O_RE::$name))
                    $result[] = array('name', $this->match);
                elseif ($this->scan(H2O_RE::$pipe)) {
                    $filtering = true;
                    $result[] = array('filter_start', $this->match);
                }
                elseif ($this->scan(H2O_RE::$seperator))
                    $result[] = array('separator', null);
                elseif ($this->scan(H2O_RE::$i18n_string))
                    $result[] = array('string', $this->match);
                elseif ($this->scan(H2O_RE::$number))
                    $result[] = array('number', $this->match);
                else
                    throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition());
            } 
            else {
                // parse filters, with chaining and ";" as filter end character
                if ($this->scan(H2O_RE::$pipe)) {
                    $result[] = array('filter_end', null);
                    $result[] = array('filter_start', null);
                }
                elseif ($this->scan(H2O_RE::$seperator))
                    $result[] = array('separator', null);
                elseif ($this->scan(H2O_RE::$filter_end)) {
                    $result[] = array('filter_end', null);
                    $filtering = false;
                }
                elseif ($this->scan(H2O_RE::$boolean))
                    $result[] = array('boolean', $this->match);
                elseif ($this->scan(H2O_RE::$named_args))
                    $result[] = array('named_argument', $this->match);
                elseif ($this->scan(H2O_RE::$name))
                    $result[] = array('name', $this->match);
                elseif ($this->scan(H2O_RE::$i18n_string))
                    $result[] = array('string', $this->match);
                elseif ($this->scan(H2O_RE::$number))
                    $result[] = array('number', $this->match);          
                else
                    throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition());
            }
        }
        // if we are still in the filter state, we add a filter_end token.
        if ($filtering)
            $result[] = array('filter_end', null);
        return $result;
    }

    # String scanner
    function scan($regexp) {
        if (preg_match($regexp . 'A', $this->source, $match, null, $this->pos)) {
            $this->match = $match[0];
            $this->pos += strlen($this->match);
            return true;
        }
        return false;
    }

    function eos() {
        return $this->pos >= strlen($this->source);
    }
    
    /**
     * return the position in the template
     */
    function getPosition() {
        return $this->fpos + $this->pos;
    }
}
?>

Well, I tried your script but it is identical to mine and the result is the same. Is there a problem in line 98?

Posted

Well, I tried your script but it is identical to mine and the result is the same. Is there a problem in line 98?

I'm not sure as I'm only learning to make modules (not going well at the moment haha)

 

But this is the code and it seems to be coming from above it if your right because this is to spit an error out.

 

        if ($until) {
            throw new TemplateSyntaxError('Unclose tag, expecting '. $until[0]);
        }
Posted

Closing as not a bug. CORE-683 and CORE-684 address this issue by checking syntax of the template when editing and by allowing undefined variables in templates respectively.

Guest
This topic is now closed to further replies.
×
×
  • Create New...