whole_func_string: function(isDoubleQuote) {
      const encapsedPart = this.node("encapsedpart");
      let curly = false;
      let result = this.node(),
        offset,
        node,
        name;
  
      // plain text
      // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1222
      if (this.token === this.tok.T_ENCAPSED_AND_WHITESPACE) {
        const text = this.text();
        this.next();
        result = result(
          "string",
          false,
          this.resolve_special_chars(text, isDoubleQuote),
          false,
          text
        );
      } else if (this.token === this.tok.T_DOLLAR_OPEN_CURLY_BRACES) {
        // dynamic variable name
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1239
        name = null;
        if (this.next().token === this.tok.T_STRING_VARNAME) {
          name = this.node("variable");
          const varName = this.text();
          this.next();
          // check if lookup an offset
          // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243
          if (this.token === "[") {
            name = name(varName, false, false);
            node = this.node("offsetlookup");
            offset = this.next().read_expr();
            this.expect("]") && this.next();
            name = node(name, offset);
          } else {
            name = varName;
          }
        } else {
          name = this.read_expr();
        }
        this.expect("}") && this.next();
        result = result("variable", name, false, true);
      } else if (this.token === this.tok.T_CURLY_OPEN) {
        // expression
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1246
        curly = true;
        result.destroy();
        result = this.next().read_variable(false, false, false);
        this.expect("}") && this.next();
      } else if (this.token === this.tok.T_VARIABLE) {
        // plain variable
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1231
        result.destroy();
        result = this.read_simple_variable(false);
  
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1233
        if (this.token === "[") {
          node = this.node("offsetlookup");
          offset = this.next().read_encaps_var_offset();
          this.expect("]") && this.next();
          result = node(result, offset);
        }
  
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1236
        if (this.token === this.tok.T_OBJECT_OPERATOR) {
          node = this.node("propertylookup");
          this.next().expect(this.tok.T_STRING);
          const what = this.node("identifier");
          name = this.text();
          this.next();
          result = node(result, what(name));
        }
  
        // error / fallback
      } else {
        this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE);
        const value = this.text();
        this.next();
        // consider it as string
        result.destroy();
        result = result("string", false, value, false, value);
      }
  
      return encapsedPart(result, curly);
    }
func_code_string: function(isDoubleQuote) {
      const encapsedPart = this.node("encapsedpart");
      let curly = false;
      let result = this.node(),
        offset,
        node,
        name;
  
      // plain text
      // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1222
      if (this.token === this.tok.T_ENCAPSED_AND_WHITESPACE) {
        const text = this.text();
        this.next();
        result = result(
          "string",
          false,
          this.resolve_special_chars(text, isDoubleQuote),
          false,
          text
        );
      } else if (this.token === this.tok.T_DOLLAR_OPEN_CURLY_BRACES) {
        // dynamic variable name
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1239
        name = null;
        if (this.next().token === this.tok.T_STRING_VARNAME) {
          name = this.node("variable");
          const varName = this.text();
          this.next();
          // check if lookup an offset
          // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243
          if (this.token === "[") {
            name = name(varName, false, false);
            node = this.node("offsetlookup");
            offset = this.next().read_expr();
            this.expect("]") && this.next();
            name = node(name, offset);
          } else {
            name = varName;
          }
        } else {
          name = this.read_expr();
        }
        this.expect("}") && this.next();
        result = result("variable", name, false, true);
      } else if (this.token === this.tok.T_CURLY_OPEN) {
        // expression
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1246
        curly = true;
        result.destroy();
        result = this.next().read_variable(false, false, false);
        this.expect("}") && this.next();
      } else if (this.token === this.tok.T_VARIABLE) {
        // plain variable
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1231
        result.destroy();
        result = this.read_simple_variable(false);
  
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1233
        if (this.token === "[") {
          node = this.node("offsetlookup");
          offset = this.next().read_encaps_var_offset();
          this.expect("]") && this.next();
          result = node(result, offset);
        }
  
        // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1236
        if (this.token === this.tok.T_OBJECT_OPERATOR) {
          node = this.node("propertylookup");
          this.next().expect(this.tok.T_STRING);
          const what = this.node("identifier");
          name = this.text();
          this.next();
          result = node(result, what(name));
        }
  
        // error / fallback
      } else {
        this.expect(this.tok.T_ENCAPSED_AND_WHITESPACE);
        const value = this.text();
        this.next();
        // consider it as string
        result.destroy();
        result = result("string", false, value, false, value);
      }
  
      return encapsedPart(result, curly);
    }
func_documentation_string: Reads and extracts an encapsed item
  ```ebnf
  encapsed_string_item ::= T_ENCAPSED_AND_WHITESPACE
  | T_DOLLAR_OPEN_CURLY_BRACES expr '}'
  | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}'
  | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
  | T_CURLY_OPEN variable '}'
  | variable
  | variable '[' expr ']'
  | variable T_OBJECT_OPERATOR T_STRING
  ```
  @return {String|Variable|Expr|Lookup}
  @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1219