Completed
Push — master ( 71a0d5...20ca54 )
by Chris
02:27
created

Parse::parseSnip()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
c 3
b 2
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
/*
3
This project is Licenced under The MIT License (MIT)
4
5
Copyright (c) 2014 Christopher Seufert
6
7
Permission is hereby granted, free of charge, to any person obtaining a copy
8
of this software and associated documentation files (the "Software"), to deal
9
in the Software without restriction, including without limitation the rights
10
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
copies of the Software, and to permit persons to whom the Software is
12
furnished to do so, subject to the following conditions:
13
14
The above copyright notice and this permission notice shall be included in
15
all copies or substantial portions of the Software.
16
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
THE SOFTWARE.
24
25
 */
26
namespace Seufert\Hamle;
27
use Seufert\Hamle\Exception\ParseError;
28
use Seufert\Hamle\Parse\Filter as ParseFilter;
29
use Seufert\Hamle\Text;
30
31
/**
32
 * HAML Enhanced - Parser, parses hamle files,
33
 * executes it and leaves a .php file to cache it
34
 *
35
 * @author Chris Seufert <[email protected]>
36
 * @package hamle
37
 */
38
class Parse {
39
  /**
40
   * @param array $indents Array of indent levels
41
   */
42
  protected $indents;
43
  /**
44
   * @var Tag[] Array of Root Document Tags
45
   */
46
  public $root;
47
  /**
48
   * @var array $lines Each Line read in from template
49
   */
50
  protected $lines;
51
  /**
52
   * Regex for parsing each HAMLE line
53
   */
54
55
  const REGEX_PARSE_LINE = <<<'ENDREGEX'
56
/^(\s*)(?:(?:([a-zA-Z0-9-]*)((?:[\.#!][\w\-\_]+)*)(\[(?:(?:\{\$[^\}]+\})?[^\\\]{]*?(?:\\.)*?(?:{[^\$])*?)+\])?)|([_\/]{1,2})|([\|:\$]\w+)|({?\$[^}]+}?)|)(?: (.*))?$/
57
ENDREGEX;
58
59
  /**
60
   * @var int Current Line Number
61
   */
62
  protected $lineNo;
63
  /**
64
   * @var int Total Lines in File
65
   */
66
  protected $lineCount;
67
68
  function __construct() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
69
    $this->init();
70
  }
71
72
  /**
73
   * Clear Lines, and Line Number, so if output is
74
   * called, no output will be produced
75
   */
76
  protected function init() {
77
    $this->lines = array();
78
    $this->lineNo = 0;
79
    $this->lineCount = 0;
80
    $this->root = array();
81
  }
82
83
  protected function loadLines($s) {
84
    $this->lines = explode("\n", str_replace("\r", "", $s));
85
    $this->lineCount = count($this->lines);
86
    $this->lineNo = 0;
87
  }
88
89
  function parseFilter(ParseFilter $filter) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
90
    foreach ($this->root as $k => $tag) {
91
      $this->root[$k] = $filter->filterTag($tag);
92
    }
93
  }
94
95
  function parseSnip($s) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
96
    //save root tags
97
    /** @var Tag[] $roots */
98
    $roots = $this->root;
99
    $this->root = array();
100
    $this->loadLines($s);
101
    $this->procLines();
102
    $this->root = array_merge($roots, $this->root);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($roots, $this->root) of type array is incompatible with the declared type array<integer,object<Seufert\Hamle\Tag>> of property $root.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
103
  }
104
105
  function applySnip() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
106
    /** @var Tag\Snippet[] $fwdSnip */
107
    $fwdSnip = array();
108
    /** @var Tag\Snippet[] $revSnip */
109
    $revSnip = array();
110
    /** @var Tag[] $roots */
111
    $roots = array();
112
    foreach ($this->root as $snip)
113
      if ($snip instanceOf Tag\Snippet) {
114
        if ($snip->getType() == "append") {
115
          array_unshift($revSnip, $snip);
116
        } else {
117
          $fwdSnip[] = $snip;
118
        }
119
      } else {
120
        $roots[] = $snip;
121
      }
122
    foreach ($fwdSnip as $snip)
123
      foreach ($roots as $root)
124
        $snip->apply($root);
125
    foreach ($revSnip as $snip)
126
      foreach ($roots as $root)
127
        $snip->apply($root);
128
    $this->root = $roots;
129
  }
130
131
  /**
132
   * Parse HAMLE template, from a string
133
   * @param string $s String to parse
134
   * @return string Parsed HAMLE as HTML
135
   */
136
  function str($s) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
137
    $this->init();
138
    $this->loadLines($s);
139
    $this->procLines();
140
  }
141
142
  function procLines() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
143
    /* @var $heir Tag[] Tag Heirachy Array */
144
    $heir = array();
145
    while ($this->lineNo < $this->lineCount) {
146
      $line = $this->lines[$this->lineNo];
147
      if (trim($line)) if (preg_match(self::REGEX_PARSE_LINE, $line, $m)) {
148
        if (FALSE !== strpos($m[1], "\t"))
149
          throw new ParseError("Tabs are not supported in templates at this time");
150
        $indent = strlen($m[1]);
151
        $tag = isset($m[2]) ? $tag = $m[2] : "";
152
        $classid = isset($m[3]) ? $m[3] : "";
153
        $params = str_replace(array('\[', '\]', '\\&'), array('[', ']', '%26'), isset($m[4]) ? $m[4] : "");
154
        $textcode = isset($m[5]) ? $m[5] : "";
155
        $text = isset($m[8]) ? $m[8] : "";
156
        $code = isset($m[6]) ? $m[6] : "";
157
        $i = self::indentLevel($indent);
158
        unset($m[0]);
159
        switch (strlen($code) ? $code[0] : ($textcode ? $textcode : "")) {
160
          case "|": //Control Tag
161
            if ($code == "|snippet")
162
              $hTag = new Tag\Snippet($text);
163
            elseif ($code == "|form")
164
              $hTag = new Tag\Form($text);
165
            elseif ($code == "|formhint")
166
              $hTag = new Tag\FormHint($text);
0 ignored issues
show
Unused Code introduced by
The call to FormHint::__construct() has too many arguments starting with $text.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
167
            elseif ($code == "|else") {
168
              $hTag = new Tag\Control(substr($code, 1), $heir[$i - 1]);
169
              $hTag->setVar($text);
170
            } else {
171
              $hTag = new Tag\Control(substr($code, 1));
172
              $hTag->setVar($text);
173
            }
174
            break;
175
          case ":": //Filter Tag
176
            $hTag = new Tag\Filter(substr($code, 1));
177
            $hTag->addContent($text, Text::TOKEN_CODE);
178
            foreach ($this->consumeBlock($indent) as $l)
179
              $hTag->addContent($l, Text::TOKEN_CODE);
180
            break;
181
          case "_": //String Tag
182
          case "__": //Unescape String Tag
183
            $hTag = new Tag\Text($textcode);
184
            $hTag->addContent($text);
185
            break;
186
          case "/": // HTML Comment
187
          case "//": // Non Printed Comment
188
            $hTag = new Tag\Comment($textcode);
189
            $hTag->addContent($text);
190
            foreach ($this->consumeBlock($indent) as $l)
191
              $hTag->addContent($l, Text::TOKEN_CODE);
192
            break;
193
          default:
194
            $attr = array();
195
            if(isset($params[0]) && $params[0] == "[") {
196
              $param = substr($params, 1, strlen($params) - 2);
197
              $param = str_replace('+','%2B', $param);
198
              $param = str_replace('\\&','%26', $param);
199
//              parse_str($param, $attr);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
200
              $attr = $this->parseQueryString($param);
201
            }
202
            $class = array(); $id = ""; $ref = "";
203
            preg_match_all('/[#\.!][a-zA-Z0-9\-\_]+/m', $classid, $cid);
204
            if (isset($cid[0])) foreach ($cid[0] as $s) {
205
              if ($s[0] == "#") $id = substr($s, 1);
206
              if ($s[0] == ".") $class[] = substr($s, 1);
207
              if ($s[0] == "!") $ref = substr($s, 1);
208
            }
209
            if($ref)
210
              $hTag = new Tag\DynHtml($tag, $class, $attr, $id, $ref);
211
            else
212
              $hTag = new Tag\Html($tag, $class, $attr, $id);
213
            $hTag->addContent($text);
214
            break;
215
        }
216
        $heir[$i] = $hTag;
217
        if ($i > 0)
218
          $heir[$i - 1]->addChild($hTag);
219
        else
220
          $this->root[] = $hTag;
221
      } else
222
        throw new ParseError("Unable to parse line {$this->lineNo}\n\"$line\"/" . preg_last_error());
223
      $this->lineNo++;
224
    }
225
  }
226
227
  function parseQueryString($qs) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
228
    $out = [];
229
    foreach(explode('&',$qs) as $s) {
230
      $kv = explode('=',$s,2);
231
      $out[urldecode($kv[0])] = isset($kv[1])?urldecode($kv[1]):null;
232
    }
233
    return $out;
234
  }
235
236
  function output() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
237
    $out = "<?php\nuse Seufert\\Hamle;\n?>";
238
    foreach ($this->root as $tag)
239
      $out .= $tag->render();
240
    return $out;
241
242
  }
243
244
  function consumeBlock($indent) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
245
    $out = array();
246
    $m = array();
247
    while ($this->lineNo + 1 < $this->lineCount &&
248
        (!trim($this->lines[$this->lineNo + 1]) ||
249
            preg_match('/^(\s){' . $indent . '}((\s)+[^\s].*)$/',
250
                $this->lines[$this->lineNo + 1], $m))) {
251
      if (trim($this->lines[$this->lineNo + 1]))
252
        $out[] = $m[2];
253
      $this->lineNo++;
254
    }
255
    return $out;
256
  }
257
258
  function indentLevel($indent) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
259
    if (!isset($this->indents)) $this->indents = array();
260
    if (!count($this->indents)) {
261
      $this->indents = array(0 => $indent);
262
      // Key = indent level, Value = Depth in spaces
263
      return 0;
264
    }
265
    foreach ($this->indents as $k => $v) {
266
      if ($v == $indent) {
267
        $this->indents = array_slice($this->indents, 0, $k + 1);
268
        return $k;
269
      }
270
    }
271
    $this->indents[] = $indent;
272
    return max(array_keys($this->indents));
273
  }
274
275
  function getLineNo() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
276
    return $this->lineNo;
277
  }
278
}
279