Evaluate.php 4.44 KB
Newer Older
Carsten  Rose's avatar
Carsten Rose committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 1/12/16
 * Time: 4:36 PM
 */

namespace qfq;

use qfq;
use qfq\store\Store;

require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/Database.php');


class Evaluate {
    private $store = null;
    private $db = null;
    private $startDelimiter = '';
    private $startDelimiterLength = 0;
    private $endDelimiter = '';
    private $endDelimiterLength = 0;
    private $sqlKeywords = array('SELECT ', 'INSERT ', 'DELETE ', 'UPDATE ', 'SHOW ');


    public function __construct(Store $store, Database $db, $startDelimiter = '{{', $endDelimiter = '}}') {
        $this->store = $store;
        $this->db = $db;
        $this->startDelimiter = $startDelimiter;
        $this->startDelimiterLength = strlen($startDelimiter);
        $this->endDelimiter = $endDelimiter;
        $this->endDelimiterLength = strlen($endDelimiter);
    }

    /**
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
     * Evaluate a whole array.
     *
     * @param $tokenArray
     * @return mixed
     */
    public function parseArray($tokenArray) {
        $arr = array();

        foreach ($tokenArray as $key => $value) {
            $arr[$key] = $this->parse($value);
        }

        return $arr;
    }

    /**
     * Recursive evaluation of 'line'.
     * Token to replaces have to be enclosed by '{{' and '}}'
     *
Carsten  Rose's avatar
Carsten Rose committed
57
     * @param $line
58
59
60
     * @param int $recursion
     * @return array|mixed|null|string
     * @throws exceptions\UserException
Carsten  Rose's avatar
Carsten Rose committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
     */
    public function parse($line, $recursion = 0) {

        if ($recursion > 4) {
            throw new qfq\exceptions\UserException("Recursion too deep: $line", ERROR_RECURSION_TOO_DEEP);
        }

        $posFirstClose = strpos($line, $this->endDelimiter);

        while ($posFirstClose !== false) {

            $posMatchOpen = strrpos(substr($line, 0, $posFirstClose), $this->startDelimiter);
            if ($posMatchOpen === false) {
                throw new \qfq\exceptions\UserException("Missing open delimiter: $line", ERROR_MISSING_OPEN_DELIMITER);
            }

            $pre = substr($line, 0, $posMatchOpen);
            $post = substr($line, $posFirstClose + $this->endDelimiterLength);
            $match = substr($line, $posMatchOpen + $this->startDelimiterLength, $posFirstClose - $posMatchOpen - $this->startDelimiterLength);

            $evaluated = $this->substitute($match);

            // If an array is returned, break everything and return this assoc array.
            if (is_array($evaluated)) {
                return $evaluated;
            }

            // More to substitute in the new evaluated result? Start recursion just with the new result..
            if (strpos($evaluated, $this->endDelimiter) !== false) {
                $evaluated = $this->parse($evaluated, $recursion + 1);
            }

            $line = $pre . $evaluated . $post;
            $posFirstClose = strpos($line, $this->endDelimiter);

        }

        return $line;
    }

    /**
     * Tries to substitute $token.
     * Token might be
104
105
106
     *   a) fetch from a store. Syntax: 'form', 'form:C', 'form:SC0', 'form:S:ALNUMX'
     *   b) a SQL statement to fire
     * The token have to be _without_ Delimiter '{{' / '}}'
Carsten  Rose's avatar
Carsten Rose committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
     * If neither a) or b) match, return the token itself, surrounded by single ticks, to emphase that substition failed.
     *
     * @param $token
     * @return array|mixed|null|string
     * @throws exceptions\DbException
     */
    public function substitute($token) {
        $sqlMode = ROW_IMPLODE_ALL;

        $token = trim($token);

        // just to extract the first token: check if this is a SQL Statement
        $arr = explode(' ', $token, 2);

        if ($token[0] === '!') {
            $token = substr($token, 1);
            $arr[0] = substr($arr[0], 1);
            $sqlMode = ROW_REGULAR;
        }

        // SQL Statement?
        if (in_array(strtoupper($arr[0] . ' '), $this->sqlKeywords)) {
            return $this->db->sql($token, $sqlMode);
        }

        // explode for: <key>:<store priority>:<sanatize class>
        $arr = explode(':', $token, 3);
        if (!isset($arr[1]))
            $arr[1] = null;
        if (!isset($arr[2]))
            $arr[2] = null;


        // search for value in stores
        $value = $this->store->getVar($arr[0], $arr[1], $arr[2]);

        // nothing replaced: put ticks around, to sanatize strings for SQL statements. Nothing to substitute is not a wished situation.
        return ($value === false) ? "'" . $token . "'" : $value;
    }
146
}