1<?php
2
3// @rodneyrehm
4// http://stackoverflow.com/a/7917979/99923
5class ParensParser
6{
7    // something to keep track of parens nesting
8    protected $stack = null;
9    // current level
10    protected $current = null;
11
12    // input string to parse
13    protected $string = null;
14    // current character offset in string
15    protected $position = null;
16    // start of text-buffer
17    protected $buffer_start = null;
18
19    public function parse($string)
20    {
21        if (!$string) {
22            // no string, no data
23            return array();
24        }
25
26        if ($string[0] == '[') {
27            // killer outer parens, as they're unnecessary
28            $string = substr($string, 1, -1);
29        }
30
31        $this->current = array();
32        $this->stack = array();
33
34        $this->string = $string;
35        $this->length = strlen($this->string);
36        // look at each character
37        for ($this->position=0; $this->position < $this->length; $this->position++) {
38            switch ($this->string[$this->position]) {
39                case '[':
40                    $this->push();
41                    // push current scope to the stack an begin a new scope
42                    array_push($this->stack, $this->current);
43                    $this->current = array();
44                    break;
45
46                case ']':
47                    $this->push();
48                    // save current scope
49                    $t = $this->current;
50                    // get the last scope from stack
51                    $this->current = array_pop($this->stack);
52                    // add just saved scope to current scope
53                    $this->current[] = $t;
54                    break;
55               /*
56                case ' ':
57                    // make each word its own token
58                    $this->push();
59                    break;
60                */
61                default:
62                    // remember the offset to do a string capture later
63                    // could've also done $buffer .= $string[$position]
64                    // but that would just be wasting resources…
65                    if ($this->buffer_start === null) {
66                        $this->buffer_start = $this->position;
67                    }
68            }
69        }
70
71        return $this->current;
72    }
73
74    protected function push()
75    {
76        if ($this->buffer_start !== null) {
77            // extract string from buffer start to current position
78            $buffer = substr($this->string, $this->buffer_start, $this->position - $this->buffer_start);
79            // clean buffer
80            $this->buffer_start = null;
81            // throw token into current scope
82            $this->current[] = $buffer;
83        }
84    }
85}
86