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
https://forums.phpfreaks.com/topic/291400-constructive-criticism-on-parsing-text/
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 }
    }
}

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);
    }

Archived

This topic is now archived and is closed to further replies.

×
×
  • 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.