Repository: viraptor/phply

Issue:
Issue with parsing HERE_DOC syntax
I've got a PHP file that has a big heredoc block in it. When I attempt to parse it, I get a recursion limit error: 

``` 
Traceback (most recent call last):
  File "php2json.py", line 31, in <module>
    output, indent=2)
  File "/Library/Python/2.7/site-packages/simplejson/__init__.py", line 276, in dump
    for chunk in iterable:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 665, in _iterencode
    for chunk in _iterencode_list(o, _current_indent_level):
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 515, in _iterencode_list
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 515, in _iterencode_list
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 634, in _iterencode_dict
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 515, in _iterencode_list
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 515, in _iterencode_list
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 634, in _iterencode_dict
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 515, in _iterencode_list
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 515, in _iterencode_list
    for chunk in chunks:
  File "/Library/Python/2.7/site-packages/simplejson/encoder.py", line 634, in _iterencode_dict
    for chunk in chunks:
```

And it goes on like that for hundreds or thousands of lines. I'm not sure what structure the data was parsed into, but it appears to be a loop of some sort. 

Discussion:

Patch:
diff --git a/phply/phpparse.py b/phply/phpparse.py
index 69d012e..12dc62b 100644
--- a/phply/phpparse.py
+++ b/phply/phpparse.py
@@ -1569,8 +1569,17 @@ def p_encaps_list_string(p):
     if p[1] == '':
         p[0] = process_php_string_escapes(p[2])
     else:
-        p[0] = ast.BinaryOp('.', p[1], process_php_string_escapes(p[2]),
-                            lineno=p.lineno(2))
+        if isinstance(p[1], string_type):
+            # if it's only a string so far, just append the contents
+            p[0] = p[1] + process_php_string_escapes(p[2])
+        elif isinstance(p[1], ast.BinaryOp) and isinstance(p[1].right, string_type):
+            # if the last right leaf is a string, extend previous binop
+            p[0] = ast.BinaryOp('.', p[1].left, p[1].right + process_php_string_escapes(p[2]),
+                                lineno=p[1].lineno)
+        else:
+            # worst case - insert a binaryop
+            p[0] = ast.BinaryOp('.', p[1], process_php_string_escapes(p[2]),
+                                lineno=p.lineno(2))
 
 def p_encaps_var(p):
     'encaps_var : VARIABLE'


Test patch:
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 0ecb9a8..62bf2cc 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -235,18 +235,12 @@ EOT;
                        BinaryOp('.',
                                 BinaryOp('.',
                                          BinaryOp('.',
-                                                  BinaryOp('.',
-                                                           BinaryOp('.',
-                                                                    BinaryOp('.',
-                                                                             'This',
-                                                                             ' is a "'),
-                                                                    Variable('$heredoc')),
-                                                           '" with some '),
-                                                  ObjectProperty(Variable('$embedded'),
-                                                                 'variables')),
-                                         '.\n'),
-                                'This'),
-                       ' is not the EOT; this is:')]),
+                                                  'This is a "',
+                                                  Variable('$heredoc')),
+                                         '" with some '),
+                                ObjectProperty(Variable('$embedded'),
+                                               'variables')),
+                       '.\nThis is not the EOT; this is:')]),
     ]
     eq_ast(input, expected)
     if sys.version_info[0] < 3: