void PHPFormatterBuffer::ProcessArray(int openParen, int closingChar)
{
    wxString whitespace = GetIndentationToLast('\n');
    int depth = 1;
    // we exit at depth 0
    phpLexerToken token;
    while(NextToken(token)) {
        if(::phpLexerIsPHPCode(m_scanner)) {
            // inside PHP block
            if(token.type == openParen) {
                ++depth;
                m_buffer << token.text;
            } else if(token.type == closingChar) {
                --depth;
                m_buffer << token.text;
                if(depth == 0) break;

            } else if(token.type == ',') {
                // New line
                RemoveLastSpace();
                m_buffer << token.text;
                m_buffer << m_options.eol;
                m_buffer << whitespace;
            } else {
                m_buffer << token.text << " ";
            }
        } else {
            // Non PHP code, copy text as is
            if(token.type == kPHP_T_CLOSE_TAG && !m_openTagWithEcho) {
                AppendEOL();
            }
            m_buffer << token.text;
        }
    }
}
void PHPFormatterBuffer::ProcessArray(int openParen, int closingChar)
{
    wxString whitespace = GetIndentationToLast('\n');
    int depth = 1;
    // we exit at depth 0
    phpLexerToken token;
    // PHPScannerCollectWhitespace whitespaceCollector(m_scanner);

    while(NextToken(token)) {
        if(::phpLexerIsPHPCode(m_scanner)) {
            // inside PHP block
            if(token.type == openParen) {
                ++depth;
                RemoveLastSpace();
                m_buffer << token.text;
            } else if(token.type == closingChar) {
                --depth;
                RemoveLastSpace();
                m_buffer << token.text;
                if(depth == 0) break;

            } else if(token.type == ',') {
                // New line
                RemoveLastSpace();
                m_buffer << token.text;
                m_buffer << m_options.eol;
                m_buffer << whitespace;

            } else if(token.type == '(' || token.type == ')' || token.type == kPHP_T_OBJECT_OPERATOR ||
                      token.type == kPHP_T_PAAMAYIM_NEKUDOTAYIM || token.type == kPHP_T_NS_SEPARATOR ||
                      token.type == kPHP_T_VARIABLE ||token.type == '[' || token.type == ']') {
                RemoveLastSpace();
                m_buffer << token.text;

            } else {
                m_buffer << token.text << " ";
            }

        } else {
            // Non PHP code, copy text as is
            if(token.type == kPHP_T_CLOSE_TAG && !m_openTagWithEcho) {
                AppendEOL();
            }
            m_buffer << token.text;
        }
    }
}
PHPFormatterBuffer& PHPFormatterBuffer::ProcessToken(const phpLexerToken& token)
{
    if(::phpLexerIsPHPCode(m_scanner)) {

        // Inside PHP tags
        if(m_insideHereDoc) {
            m_buffer << token.text;
            if(token.type == kPHP_T_END_HEREDOC) {
                AppendEOL();
                if(m_options.flags & kPFF_BreakAfterHeredoc) {
                    AppendEOL();
                }
                m_insideHereDoc = false;
            }
        } else if(token.type == kPHP_T_OPEN_TAG) {
            m_openTagWithEcho = false;
            m_buffer << token.text;
            AppendEOL();
            AppendEOL();

        } else if(token.type == kPHP_T_OPEN_TAG_WITH_ECHO) {
            m_openTagWithEcho = true;
            m_buffer << token.text << " ";

        } else if(token.type == '{') {
            HandleOpenCurlyBrace();

        } else if(token.type == kPHP_T_VARIABLE) {
            m_buffer << token.text << " ";

        } else if(token.type == kPHP_T_FOREACH) {
            m_buffer << token.text << " ";
            if(m_options.flags & kPFF_BreakBeforeForeach) {
                InsertSeparatorLine();
            }
        } else if(token.type == kPHP_T_ELSE || token.type == kPHP_T_ELSEIF) {
            if(m_options.flags & kPFF_ElseOnSameLineAsClosingCurlyBrace && m_lastToken.type == '}') {
                ReverseClearUntilFind("}");
                m_buffer << " " << token.text << " ";
            } else {
                m_buffer << token.text << " ";
            }

        } else if(token.type == kPHP_T_WHILE) {
            m_buffer << token.text << " ";
            if(m_options.flags & kPFF_BreakBeforeWhile) {
                InsertSeparatorLine();
            }
        } else if(token.type == kPHP_T_CLASS) {
            if(m_options.flags & kPFF_BreakBeforeClass) {
                InsertSeparatorLine();
            }
            m_buffer << token.text << " ";

        } else if(token.type == kPHP_T_FUNCTION) {
            // Found a function
            m_buffer << token.text << " ";
            if(m_lastCommentLine != token.lineNumber) {
                // this function has no comment associated with it
                if(m_options.flags & kPFF_BreakBeforeFunction) {
                    // But don't insert new line separator if this keyword
                    // was found in a statement like:
                    // use function A as B;
                    if(m_lastToken.type != kPHP_T_USE) {
                        // Look backward until we find the last EOL
                        // and insert another one...
                        InsertSeparatorLine();
                    }
                }
            }
        } else if(token.type == kPHP_T_START_HEREDOC) {
            RemoveLastSpace();
            m_buffer << token.text;
            m_insideHereDoc = true;

        } else if(token.type == kPHP_T_FOR) {
            // for(;;) is a special case where
            // we don't insert new line after semi-colon
            m_insideForStatement = true;
            m_buffer << token.text;

        } else if(token.type == '(' && m_insideForStatement) {
            m_forDepth++;
            m_buffer << token.text;

        } else if(token.type == ')' && m_insideForStatement) {
            m_forDepth--;
            if(m_forDepth == 0) {
                m_insideForStatement = false;
            }
            m_buffer << token.text;

        } else if(token.type == '(' || token.type == '[') {
            RemoveLastSpace();
            m_buffer << token.text;

            if(m_options.flags & kPFF_VerticalArrays && token.type == '(' && m_lastToken.type == kPHP_T_ARRAY &&
                    m_parenDepth == 1) {
                ProcessArray('(', ')');
            }

        } else if(token.type == ')') {
            RemoveLastSpace();
            m_buffer << token.text;

        } else if(token.type == ']') {
            RemoveLastSpace();
            m_buffer << token.text << " ";

        } else if(token.type == ';') {
            RemoveLastSpace();
            m_buffer << token.text;
            AppendEOL();

        } else if(token.type == '}') {
            UnIndent();
            m_buffer << token.text;
            AppendEOL(kDepthDec);

        } else if(token.type == ',') {
            RemoveLastSpace();
            m_buffer << token.text << " ";

        } else if(token.type == '.') {
            m_buffer << token.text << " ";
            if(m_options.flags & kPFF_BreakAfterStringConcatentation && (m_parenDepth == 1)) {
                // inside a function call
                wxString whitepace = GetIndentationToLast('(');
                if(!whitepace.IsEmpty()) {
                    m_buffer << m_options.eol;
                    m_buffer << whitepace;
                }
            }
        } else if(token.type == '&') {
            // attach reference to the next token
            m_buffer << token.text;

        } else if(token.type == kPHP_T_CXX_COMMENT) {
            // C++ style comment ("//")
            // AppendEOL();
            m_buffer << token.text;
            AppendEOL();
            m_lastCommentLine = token.lineNumber + 1;

        } else if(token.type == kPHP_T_C_COMMENT) {
            // Doxygen style comment: we first format it to match
            // the current indentation + line ending before adding
            // it to the current buffer
            if(m_parenDepth == 0) {
                AppendEOL();
                m_buffer << FormatDoxyComment(token.text);
                AppendEOL();
                m_lastCommentLine = token.endLineNumber + 1;
            } else {
                m_buffer << token.text << " ";
            }

        } else if(token.type == kPHP_T_OBJECT_OPERATOR || token.type == kPHP_T_PAAMAYIM_NEKUDOTAYIM) {
            // -> operator or ::
            RemoveLastSpace();
            m_buffer << token.text;

        } else if(token.type == '!') {
            // dont add extrace space after the NOT operator
            m_buffer << token.text;

        } else if(token.type == kPHP_T_NS_SEPARATOR) {
            if(m_lastToken.type == kPHP_T_IDENTIFIER) {
                RemoveLastSpace();
            }
            m_buffer << token.text;
        } else {
            // by default, add the token text and a space after it
            m_buffer << token.text << " ";
        }

        m_lastToken = token;

    } else {
        if(token.type == kPHP_T_CLOSE_TAG && !m_openTagWithEcho) {
            AppendEOL();
        }
        m_buffer << token.text;
    }
    return *this;
}
void PHPFormatterBuffer::format()
{
    phpLexerToken token;
    phpLexerToken::Vet_t sequence;
    m_stack.push(sequence);
    m_sequence = &m_stack.top();
    while(NextToken(token)) {
        phpLexerToken nextToken;
        if(::phpLexerIsPHPCode(m_scanner)) {
            ProcessToken(token);

            //
            // Special indentation cases
            // Handle the following:
            // for(..) <statement> -> for(..)
            //                            <statement>
            // if(..) <statement> -> if(..)
            //                            <statement>
            // etc.
            // In addtion, we also handle here the following:
            // if(something) {} else <statement> =>
            //                                      if(something) {
            //                                      } else
            //                                          <statement>
            //

            if(token.type == '(') {
                // Create new stack
                m_stack.push(phpLexerToken::Vet_t());
                m_sequence = &m_stack.top();
            }
            if(token.type == ')') {
                // switch back to the previous sequence
                if(m_stack.size() >= 2) {
                    m_stack.pop();
                    m_sequence = &m_stack.top();
                }

                if(!m_sequence->empty()) {
                    phpLexerToken lastToken = m_sequence->at(m_sequence->size() - 1);
                    // The following tokens are usually followed by an open brace
                    if(lastToken.type == kPHP_T_IF || lastToken.type == kPHP_T_FOR || lastToken.type == kPHP_T_ELSEIF ||
                            lastToken.type == kPHP_T_FOREACH || lastToken.type == kPHP_T_WHILE) {
                        // Peek at the next char
                        if(PeekToken(nextToken)) {
                            if(nextToken.type != '{' && !nextToken.IsAnyComment()) {
                                // Increase the depth but only temporarily for the next statement
                                AppendEOL(kDepthIncTemporarily);
                            }
                        }
                    }
                }
            } else if(token.type == kPHP_T_ELSE) {
                // Check for 'else <statement>' (without an open  curly braces)
                if(PeekToken(nextToken) && nextToken.type != '{' && nextToken.type != kPHP_T_IF) {
                    AppendEOL(kDepthIncTemporarily);
                }

            } else {
                m_sequence->push_back(token);
            }
        } else {
            ProcessToken(token);
        }
    }
}
void PHPFormatterBuffer::HandleOpenCurlyBrace()
{
    RemoveLastSpace();
    m_buffer << " {";
    AppendEOL(kDepthInc);
}