Parser::parse()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
namespace Ayeo\Parser;
3
4
use Ayeo\Parser\Utils\Camelizer;
5
6
class Parser
7
{
8
    /**
9
     * @var array
10
     */
11
    private $data;
12
13
    /**
14
     * @var string
15
     */
16
    private $prefix;
17
18
    /**
19
     * @var string
20
     */
21
    private $pattern;
22
23
    /**
24
     * @var Camelizer
25
     */
26
    private $camelizer;
27
28
    /**
29
     * @param string $prefix
30
     * @param string $open
31
     * @param string $close
32
     */
33
    public function __construct($prefix = '', $open = "{{", $close = "}}")
34
    {
35
        $this->prefix = $prefix;
36
        $this->setEmbraceStrings($open, $close);
37
    }
38
39
    /**
40
     * @param string $template
41
     * @param array $data
42
     * @return string
43
     */
44
    public function parse($template, array $data)
45
    {
46
        $this->data = $data;
47
        $this->camelizer = new Camelizer();
48
        return preg_replace_callback($this->pattern, [$this, 'callback'], $template);
49
    }
50
51
    /**
52
     * @param string $open
53
     * @param string $close
54
     */
55
    public function setEmbraceStrings($open, $close)
56
    {
57
        if (preg_match("@#@", $open))
58
        {
59
            throw new \LogicException("Open delimiter must not contain # char");
60
        }
61
62
        if (preg_match("@#@", $close))
63
        {
64
            throw new \LogicException("Close delimiter must not contain # char");
65
        }
66
67
        $this->pattern = sprintf("#%s(.+?)%s#", preg_quote($open), preg_quote($close));
68
    }
69
70
    /**
71
     * @param array $matches
72
     * @return array|mixed
73
     * @throws \Exception
74
     */
75
    private function callback(array $matches)
76
    {
77
        $places = explode('.', $this->prefix.$matches[1]);
78
79
        $c = $this->data;
80
        $processedPath = [];
81
        foreach ($places as $place)
82
        {
83
            $processedPath[] = $place;
84
85
            if (is_object($c))
86
            {
87
                $reflection = new \ReflectionClass(get_class($c));
88
                $methodName = 'get'.$this->camelizer->camelize($place);
89
90
                try
91
                {
92
                    $property = $reflection->getProperty($place);
93
                }
94
                catch (\Exception $e)
95
                {
96
                    $property = null;
97
                }
98
99
100
                if ($property && $property->isPublic())
101
                {
102
                    $c = $c->{$place};
103
                }
104
                else if ($reflection->hasMethod($methodName))
105
                {
106
                    $c = call_user_func(array($c, $methodName));
107
                }
108
                else
109
                {
110
                    $message = "Class %s has not property or getter for: %s. Full path: %s";
111
                    throw new \Exception(sprintf($message, get_class($c), $place, join('.', $places)));
112
                }
113
            }
114
            else if (is_array($c))
115
            {
116
                $c = $c[$place];
117
            }
118
119
            if (is_null($c))
120
            {
121
                throw new \Exception('Null value for: '.join('.', $processedPath));
122
            }
123
        }
124
125
        return $c;
126
    }
127
}
128