Jump to content

Constructive criticism on parsing text?


NotionCommotion

Recommended Posts

I need to parse some text similar to Twig templates, but MUCH more limited and error detection is not required.  My reason for not using Twig is Twig seems overkill.  All it needs to do is replace variables and simple ternary operators (unlike Twig, the ternary operators use ! instead of "not").

 

I would welcome any constructive criticism and/or recommended changes on how I implemented it.

 

Thanks

<?php
date_default_timezone_set('America/Los_Angeles');
ini_set('display_errors', 1);
error_reporting(E_ALL);

/*
Replaces all values surrounded by {{   }} deliminators.
Handles direct variable.
Multiple variables can be separated by the ~ symbol.
Also does (non-nested) ternary operators: x?y, !x?y, x==1?y, x?'y', x?y:z
*/

$template='
Test1  Hello {{ firstname }} {{ lastname }}.  Call me in {{ days_int }} or {{ days_string }} days.<br>
Test2  {{ flag_int?"do this2" }}<br>
Test3  {{ flag_int?"do this3":"do that3" }}<br>
Test4  {{ value_int==5?"do this4" }}<br>
Test5  {{ value_int==5?"do this5":"do that5" }}<br>
Test6  {{ value_int==4?"do this6" }}<br>
Test7  {{ value_int==4?"do this7":"do that7" }}<br>
Test8  {{ value_int!=5?"do this8" }}<br>
Test9  {{ value_int!=5?"do this9":"do that9" }}<br>
Test10 {{ value_int!=4?"do this10" }}<br>
Test11 {{ value_int!=4?"do this11":"do that11" }}<br>
Test12 {{ value_int!=4 bla "do this12":"do that12" }}<br>
Test13 {{ value_int==5?"do this5 to "~firstname:"do that5 to "~firstname }}<br>
Test14 {{ !flag_int?"do this14" }}<br>
Test15  Hello {{ firstname~" "~lastname~".  How are things" }}.<br>
';

$values=array(
    'firstname'=>'John',
    'lastname'=>'Doe',
    'days_int'=>5,
    'days_string'=>6,
    'flag_int'=>true,
    'value_int'=>5
);

$parser=new parser();
echo($parser->parse($template,$values));

class parser
{
    public function parse($template,$values)
    {
        return preg_replace_callback('/\{\{\ (.+?)\ \}\}/',function ($matches) use ($values) {
            $ternary = explode("?", $matches[1]);
            if(count($ternary)>1) {
                //Ternary operator. $ternary[0] is the condition and $ternary[1] is the resulting value(s)
                $conditions=explode("==", $ternary[0]);
                if(count($conditions)>1){
                    //Equal Condition
                    $cond=($this->getVal($conditions[0],$values)==$this->getVal($conditions[1],$values));
                }
                else {
                    $conditions=explode("!=", $ternary[0]);
                    if(count($conditions)>1){
                        //Not Equal Condition
                        $cond=!($this->getVal($conditions[0],$values)==$this->getVal($conditions[1],$values));
                    }
                    else {
                        //A flag
                        $cond=($conditions[0]== "!") ? !($this->getVal(ltrim($conditions[0],'!'),$values)) : ($this->getVal($conditions[0],$values));
                    }
                }
                $options=(explode(':',$ternary[1]));
                return $cond?$this->getValues($options[0],$values):(isset($options[1])?$this->getValues($options[1],$values):null);
            }
            else {return $this->getValues($matches[1],$values);}
            },
            $template);
    }

    private function getVal($s,$values)
    {
        return in_array($s[0],array('\'','"'))?substr($s,1,strlen($s)-2):(isset($values[$s])?$values[$s]:$s);
    }
    private function getValues($strings, $values){
        $s=null;
        foreach(explode('~',$strings) as $string){
            $s.=$this->getVal($string,$values);
        };
        return $s;
    }
}

?>
Link to comment
Share on other sites

This is going to be more difficult than I thought.  Suppose I wish to parse the following JSON where noInvalid is true.

{
    "rules":{
        "name":{"required":true,{{ noInvalid?'"noInvalid":true,' }}"maxlength":90,"minlength":2 }
    }
}

I am looking for the following, however, my class incorrectly splits on the colon between "noInvalid" and true.  Furthermore, the text above is not valid JSON, and is hard to troubleshoot.

{
    "rules":{
        "name":{"required":true,"noInvalid":true,"maxlength":90,"minlength":2 }
    }
}

I suppose I should either go back to creating it directly from PHP, or maybe get rid of my ternary operator, and go with:

{
    "rules":{
        "name":{"required":true,"noInvalid": "{{ noInvalid }}", "maxlength":90,"minlength":2 }
    }
}

Problem is to make the original file valid JSON, I need to put quotes around my variables which might cause problems with Boolean values

{
    "rules":{
        "name":{"required":true,"noInvalid": "1", "maxlength":90,"minlength":2 }
    }
}
Link to comment
Share on other sites

Ended up keeping it really simple.  Just replace the values.  Used "{ }" and '{ }' as a deliminator so it is valid JSON.  It will not allow a text value to be parsed in without quotes, so maybe I will add.


    final public function parse($template, array $values) {
        //return preg_replace_callback('/\{"{\ (\w+)\ \}\"/',function ($matches) use ($values) {
        $re = <<<RE
/
    "{ (\w+) }"
    |
    '{ (\w+) }'
/x
RE;
        return preg_replace_callback($re,function ($matches) use ($values) {
            if(isset($values[$matches[1]])){
                if(is_bool($values[$matches[1]])){$new=($values[$matches[1]]?'true':'false');}
                elseif(is_numeric($values[$matches[1]])){$new=$values[$matches[1]];}
                else{$new=$values[$matches[1]];}
            }
            else{$new=$matches[0];}
            return $new;
            },
            $template);
    }
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.