<?php
/**
 * A test to ensure that arrays conform to the array coding standard.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Marc McIntyre <mmcintyre@squiz.net>
 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

/**
 * A test to ensure that arrays conform to the array coding standard.
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Marc McIntyre <mmcintyre@squiz.net>
 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
 * @version   Release: @package_version@
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */
class Ongr_Sniffs_Arrays_ArrayDeclarationSniff implements PHP_CodeSniffer_Sniff
{

    /**
     * Returns an array of tokens this test wants to listen for.
     *
     * @return array
     */
    public function register()
    {
        return array(
            T_ARRAY,
            T_OPEN_SHORT_ARRAY,
        );

    }//end register()

    /**
     * Processes this sniff, when one of its tokens is encountered.
     *
     * @param PHP_CodeSniffer_File $phpcsFile The current file being checked.
     * @param int                  $stackPtr  The position of the current token in
     *                                        the stack passed in $tokens.
     *
     * @return void
     */
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();
        //Ongr !== T_OPEN_SHORT_ARRAY
        if ($tokens[$stackPtr]['code'] !== T_OPEN_SHORT_ARRAY) {
            $phpcsFile->addError('Must use short array syntax for arrays', $stackPtr, 'LongArray');

            // Array keyword should be lower case.
            if ($tokens[$stackPtr]['content'] !== strtolower($tokens[$stackPtr]['content'])) {
                if ($tokens[$stackPtr]['content'] === strtoupper($tokens[$stackPtr]['content'])) {
                    $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'upper');
                } else {
                    $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'mixed');
                }

                $error = 'Array keyword should be lower case; expected "array" but found "%s"';
                $data  = array($tokens[$stackPtr]['content']);
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NotLowerCase', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->replaceToken($stackPtr, 'array');
                }
            } else {
                $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'lower');
            }

            $arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
            $arrayEnd   = $tokens[$arrayStart]['parenthesis_closer'];

            if ($arrayStart !== ($stackPtr + 1)) {
                $error = 'There must be no space between the "array" keyword and the opening parenthesis';
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword');

                if ($fix === true) {
                    $phpcsFile->fixer->beginChangeset();
                    for ($i = ($stackPtr + 1); $i < $arrayStart; $i++) {
                        $phpcsFile->fixer->replaceToken($i, '');
                    }

                    $phpcsFile->fixer->endChangeset();
                }
            }
        } else {
            $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
            $arrayStart = $stackPtr;
            $arrayEnd   = $tokens[$stackPtr]['bracket_closer'];
        }//end if

        // Check for empty arrays.
        $content = $phpcsFile->findNext(T_WHITESPACE, ($arrayStart + 1), ($arrayEnd + 1), true);
        if ($content === $arrayEnd) {
            // Empty array, but if the brackets aren't together, there's a problem.
            if (($arrayEnd - $arrayStart) !== 1) {
                $error = 'Empty array declaration must have no space between the parentheses';
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceInEmptyArray');

                if ($fix === true) {
                    $phpcsFile->fixer->beginChangeset();
                    for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
                        $phpcsFile->fixer->replaceToken($i, '');
                    }

                    $phpcsFile->fixer->endChangeset();
                }

                // We can return here because there is nothing else to check. All code
                // below can assume that the array is not empty.
                return;
            }
        }

        if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
            $this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd);
        } else {
            $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd);
        }

    }//end process()

    /**
     * Processes a single-line array definition.
     *
     * @param PHP_CodeSniffer_File $phpcsFile  The current file being checked.
     * @param int                  $stackPtr   The position of the current token
     *                                         in the stack passed in $tokens.
     * @param int                  $arrayStart The token that starts the array definition.
     * @param int                  $arrayEnd   The token that ends the array definition.
     *
     * @return void
     */
    public function processSingleLineArray(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $arrayStart, $arrayEnd)
    {
        $tokens = $phpcsFile->getTokens();

        // Check if there are multiple values. If so, then it has to be multiple lines
        // unless it is contained inside a function call or condition.
        $valueCount = 0;
        $commas     = array();
        for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
            // Skip bracketed statements, like function calls.
            if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
                $i = $tokens[$i]['parenthesis_closer'];
                continue;
            }

            if ($tokens[$i]['code'] === T_COMMA) {
                // Before counting this comma, make sure we are not
                // at the end of the array.
                $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), $arrayEnd, true);
                if ($next !== false) {
                    $valueCount++;
                    $commas[] = $i;
                } else {
                    // There is a comma at the end of a single line array.
                    $error = 'Comma not allowed after last value in single-line array declaration';
                    $fix   = $phpcsFile->addFixableError($error, $i, 'CommaAfterLast');
                    if ($fix === true) {
                        $phpcsFile->fixer->replaceToken($i, '');
                    }
                }
            }
        }//end for

        // Now check each of the double arrows (if any).
        $nextArrow = $arrayStart;
        while (($nextArrow = $phpcsFile->findNext(T_DOUBLE_ARROW, ($nextArrow + 1), $arrayEnd)) !== false) {
            if ($tokens[($nextArrow - 1)]['code'] !== T_WHITESPACE) {
                $content = $tokens[($nextArrow - 1)]['content'];
                $error   = 'Expected 1 space between "%s" and double arrow; 0 found';
                $data    = array($content);
                $fix     = $phpcsFile->addFixableError($error, $nextArrow, 'NoSpaceBeforeDoubleArrow', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->addContentBefore($nextArrow, ' ');
                }
            } else {
                $spaceLength = $tokens[($nextArrow - 1)]['length'];
                if ($spaceLength !== 1) {
                    $content = $tokens[($nextArrow - 2)]['content'];
                    $error   = 'Expected 1 space between "%s" and double arrow; %s found';
                    $data    = array(
                        $content,
                        $spaceLength,
                    );

                    $fix = $phpcsFile->addFixableError($error, $nextArrow, 'SpaceBeforeDoubleArrow', $data);
                    if ($fix === true) {
                        $phpcsFile->fixer->replaceToken(($nextArrow - 1), ' ');
                    }
                }
            }//end if

            if ($tokens[($nextArrow + 1)]['code'] !== T_WHITESPACE) {
                $content = $tokens[($nextArrow + 1)]['content'];
                $error   = 'Expected 1 space between double arrow and "%s"; 0 found';
                $data    = array($content);
                $fix     = $phpcsFile->addFixableError($error, $nextArrow, 'NoSpaceAfterDoubleArrow', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->addContent($nextArrow, ' ');
                }
            } else {
                $spaceLength = $tokens[($nextArrow + 1)]['length'];
                if ($spaceLength !== 1) {
                    $content = $tokens[($nextArrow + 2)]['content'];
                    $error   = 'Expected 1 space between double arrow and "%s"; %s found';
                    $data    = array(
                        $content,
                        $spaceLength,
                    );

                    $fix = $phpcsFile->addFixableError($error, $nextArrow, 'SpaceAfterDoubleArrow', $data);
                    if ($fix === true) {
                        $phpcsFile->fixer->replaceToken(($nextArrow + 1), ' ');
                    }
                }
            }//end if
        }//end while

        if ($valueCount > 0) {

            //ONGR We allow declaring values on a single line
//            $conditionCheck = $phpcsFile->findPrevious(array(T_OPEN_PARENTHESIS, T_SEMICOLON), ($stackPtr - 1), null, false);
//
//            if ($conditionCheck === false
//                || $tokens[$conditionCheck]['line'] !== $tokens[$stackPtr]['line']
//            ) {
//
//                $error = 'Array with multiple values cannot be declared on a single line';
//                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SingleLineNotAllowed');
//                if ($fix === true) {
//                    $phpcsFile->fixer->beginChangeset();
//                    $phpcsFile->fixer->addNewline($arrayStart);
//                    $phpcsFile->fixer->addNewlineBefore($arrayEnd);
//                    $phpcsFile->fixer->endChangeset();
//                }
//
//                return;
//            }

            // We have a multiple value array that is inside a condition or
            // function. Check its spacing is correct.
            foreach ($commas as $comma) {
                if ($tokens[($comma + 1)]['code'] !== T_WHITESPACE) {
                    $content = $tokens[($comma + 1)]['content'];
                    $error   = 'Expected 1 space between comma and "%s"; 0 found';
                    $data    = array($content);
                    $fix     = $phpcsFile->addFixableError($error, $comma, 'NoSpaceAfterComma', $data);
                    if ($fix === true) {
                        $phpcsFile->fixer->addContent($comma, ' ');
                    }
                } else {
                    $spaceLength = $tokens[($comma + 1)]['length'];
                    if ($spaceLength !== 1) {
                        $content = $tokens[($comma + 2)]['content'];
                        $error   = 'Expected 1 space between comma and "%s"; %s found';
                        $data    = array(
                            $content,
                            $spaceLength,
                        );

                        $fix = $phpcsFile->addFixableError($error, $comma, 'SpaceAfterComma', $data);
                        if ($fix === true) {
                            $phpcsFile->fixer->replaceToken(($comma + 1), ' ');
                        }
                    }
                }//end if

                if ($tokens[($comma - 1)]['code'] === T_WHITESPACE) {
                    $content     = $tokens[($comma - 2)]['content'];
                    $spaceLength = $tokens[($comma - 1)]['length'];
                    $error       = 'Expected 0 spaces between "%s" and comma; %s found';
                    $data        = array(
                        $content,
                        $spaceLength,
                    );

                    $fix = $phpcsFile->addFixableError($error, $comma, 'SpaceBeforeComma', $data);
                    if ($fix === true) {
                        $phpcsFile->fixer->replaceToken(($comma - 1), '');
                    }
                }
            }//end foreach
        }//end if

    }//end processSingleLineArray()

    /**
     * Processes a multi-line array definition.
     *
     * @param PHP_CodeSniffer_File $phpcsFile  The current file being checked.
     * @param int                  $stackPtr   The position of the current token
     *                                         in the stack passed in $tokens.
     * @param int                  $arrayStart The token that starts the array definition.
     * @param int                  $arrayEnd   The token that ends the array definition.
     *
     * @return void
     */
    public function processMultiLineArray(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $arrayStart, $arrayEnd)
    {
        $tokens       = $phpcsFile->getTokens();
        $keywordStart = $tokens[$stackPtr]['column'];
        $statementStart = $this->getStatementStartColumn($phpcsFile, $stackPtr);
        // Check the closing bracket is on a new line.
        $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($arrayEnd - 1), $arrayStart, true);
        if ($tokens[$lastContent]['line'] === $tokens[$arrayEnd]['line']) {
            $error = 'Closing parenthesis of array declaration must be on a new line';
            $fix   = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNewLine');
            if ($fix === true) {
                $phpcsFile->fixer->addNewlineBefore($arrayEnd);
            }
        } else if ($tokens[$arrayEnd]['column'] !== $statementStart) {
            // Check the closing bracket is lined up under the "a" in array.
            $expected = $statementStart;
            $found    = ($tokens[$arrayEnd]['column']);
            $error    = 'Closing parenthesis not aligned correctly; expected %s space(s) but found %s';
            $data     = array(
                $expected,
                $found,
            );
            $phpcsFile->addError($error, $arrayEnd, 'CloseBraceNotAligned', $data);
//            $fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNotAligned', $data);
//            if ($fix === true) {
//                if ($found === 0) {
//                    $phpcsFile->fixer->addContent(($arrayEnd - 1), str_repeat(' ', $expected));
//                } else {
//                    $phpcsFile->fixer->replaceToken(($arrayEnd - 1), str_repeat(' ', $expected));
//                }
//            }
        }//end if

        $nextToken  = $stackPtr;
        $keyUsed    = false;
        $singleUsed = false;
        $indices    = array();
        $maxLength  = 0;

        if ($tokens[$stackPtr]['code'] === T_ARRAY) {
            $lastToken = $tokens[$stackPtr]['parenthesis_opener'];
        } else {
            $lastToken = $stackPtr;
        }

        // Find all the double arrows that reside in this scope.
        for ($nextToken = ($stackPtr + 1); $nextToken < $arrayEnd; $nextToken++) {
            // Skip bracketed statements, like function calls.
            if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS
                && (isset($tokens[$nextToken]['parenthesis_owner']) === false
                    || $tokens[$nextToken]['parenthesis_owner'] !== $stackPtr)
            ) {
                $nextToken = $tokens[$nextToken]['parenthesis_closer'];
                continue;
            }

            if ($tokens[$nextToken]['code'] === T_ARRAY) {
                // Let subsequent calls of this test handle nested arrays.
                $indices[] = array('value' => $nextToken);
                $lastToken = $nextToken;
                $nextToken = $tokens[$tokens[$nextToken]['parenthesis_opener']]['parenthesis_closer'];
                $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
                if ($tokens[$nextToken]['code'] !== T_COMMA) {
                    $nextToken--;
                } else {
                    $lastToken = $nextToken;
                }

                continue;
            }

            //Ongr
            if ($tokens[$nextToken]['code'] === T_ARRAY || $tokens[$nextToken]['code'] == T_OPEN_SHORT_ARRAY
                || $tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS
            ) {
                // Skip contents in parenthesis and let subsequent calls of this test handle nested arrays.
                if ($tokens[$nextToken - 2]['code'] == T_CLOSURE) {
                    $nextToken -= 2;
                }
                $indices[] = ['value' => $nextToken];
                if ($tokens[$nextToken]['code'] == T_OPEN_SHORT_ARRAY) {
                    $nextToken = $tokens[$nextToken]['bracket_closer'];
                } else {
                    $nextToken = $tokens[$nextToken]['parenthesis_closer'];
                }
                // If array declaration is the only thing on last line other checks
                // will not have any chance to check for comma so we need to do it now.
                $multiLine = $tokens[$arrayStart]['line'] !== $tokens[$arrayEnd]['line'];
                $next = $phpcsFile->findNext(
                    [T_DOUBLE_ARROW, T_COMMA, T_ARRAY, T_OPEN_SHORT_ARRAY, T_OPEN_PARENTHESIS],
                    $nextToken,
                    $arrayEnd
                );
                if ($multiLine && !$next) {
                    // There is no more content which means missing comma.
                    $error = 'Each line in an array declaration must end with a comma';
                    $phpcsFile->addError($error, $nextToken, 'NoCommaAfterLast');
                    return;
                }

                continue;
            }

            if ($tokens[$nextToken]['code'] !== T_DOUBLE_ARROW
                && $tokens[$nextToken]['code'] !== T_COMMA
            ) {
                continue;
            }

            $currentEntry = array();

            if ($tokens[$nextToken]['code'] === T_COMMA) {
                $stackPtrCount = 0;
                if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
                    $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
                }

                $commaCount = 0;
                if (isset($tokens[$nextToken]['nested_parenthesis']) === true) {
                    $commaCount = count($tokens[$nextToken]['nested_parenthesis']);
                    if ($tokens[$stackPtr]['code'] === T_ARRAY) {
                        // Remove parenthesis that are used to define the array.
                        $commaCount--;
                    }
                }

                if ($commaCount > $stackPtrCount) {
                    // This comma is inside more parenthesis than the ARRAY keyword,
                    // then there it is actually a comma used to separate arguments
                    // in a function call.
                    continue;
                }

                if ($keyUsed === true && $tokens[$lastToken]['code'] === T_COMMA) {
                    $error = 'No key specified for array entry; first entry specifies key';
                    $phpcsFile->addError($error, $nextToken, 'NoKeySpecified');
                    return;
                }

                if ($keyUsed === false) {
                    if ($tokens[($nextToken - 1)]['code'] === T_WHITESPACE) {
                        $content = $tokens[($nextToken - 2)]['content'];
                        if ($tokens[($nextToken - 1)]['content'] === $phpcsFile->eolChar) {
                            $spaceLength = 'newline';
                        } else {
                            $spaceLength = $tokens[($nextToken - 1)]['length'];
                        }

                        $error = 'Expected 0 spaces between "%s" and comma; %s found';
                        $data  = array(
                            $content,
                            $spaceLength,
                        );

                        $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpaceBeforeComma', $data);
                        if ($fix === true) {
                            $phpcsFile->fixer->replaceToken(($nextToken - 1), '');
                        }
                    }

                    $valueContent = $phpcsFile->findNext(T_WHITESPACE, ($lastToken + 1), $nextToken, true);
                    $indices[]    = array('value' => $valueContent);
                    $singleUsed   = true;
                }//end if

                $lastToken = $nextToken;
                continue;
            }//end if

            if ($tokens[$nextToken]['code'] === T_DOUBLE_ARROW) {
                if ($singleUsed === true) {
                    $error = 'Key specified for array entry; first entry has no key';
                    $phpcsFile->addError($error, $nextToken, 'KeySpecified');
                    return;
                }
                // Ongr we require single whitespace before double arrow
                if ($tokens[$nextToken - 1]['code'] !== T_WHITESPACE
                    || strlen($tokens[$nextToken - 1]['content']) != 1
                ) {
                    $error = 'Expected single whitespace before double arrow, found %s spaces';
                    if ($tokens[$nextToken - 1]['code'] !== T_WHITESPACE) {
                        $data = [0];
                    } else {
                        $data = [strlen($tokens[$nextToken - 1]['content'])];
                    }
                    $phpcsFile->addError($error, $nextToken - 1, 'DoubleArrowSpacing', $data);
                }

                $currentEntry['arrow'] = $nextToken;
                $keyUsed = true;

                // Find the start of index that uses this double arrow.
                $indexEnd   = $phpcsFile->findPrevious(T_WHITESPACE, ($nextToken - 1), $arrayStart, true);
                $indexStart = $phpcsFile->findStartOfStatement($indexEnd);

                if ($indexStart === $indexEnd) {
                    $currentEntry['index']         = $indexEnd;
                    $currentEntry['index_content'] = $tokens[$indexEnd]['content'];
                } else {
                    $currentEntry['index']         = $indexStart;
                    $currentEntry['index_content'] = $phpcsFile->getTokensAsString($indexStart, ($indexEnd - $indexStart + 1));
                }

                $indexLength = strlen($currentEntry['index_content']);
                if ($maxLength < $indexLength) {
                    $maxLength = $indexLength;
                }

                // Find the value of this index.
                $nextContent           = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), $arrayEnd, true);
                $currentEntry['value'] = $nextContent;
                $indices[] = $currentEntry;
                $lastToken = $nextToken;
            }//end if
        }//end for

        //Ongr we allow multi-line arrays, that should be single-line
        // Check for mutli-line arrays that should be single-line.
//        $singleValue = false;
//
//        if (empty($indices) === true) {
//            $singleValue = true;
//        } else if (count($indices) === 1 && $tokens[$lastToken]['code'] === T_COMMA) {
//            // There may be another array value without a comma.
//            $exclude     = PHP_CodeSniffer_Tokens::$emptyTokens;
//            $exclude[]   = T_COMMA;
//            $nextContent = $phpcsFile->findNext($exclude, ($indices[0]['value'] + 1), $arrayEnd, true);
//            if ($nextContent === false) {
//                $singleValue = true;
//            }
//        }
//
//        if ($singleValue === true) {
        // Array cannot be empty, so this is a multi-line array with
        // a single value. It should be defined on single line.
        $error = 'Multi-line array contains a single value; use single-line array instead';
//            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'MultiLineNotAllowed');
//
//            if ($fix === true) {
//                $phpcsFile->fixer->beginChangeset();
//                for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
//                    if ($tokens[$i]['code'] !== T_WHITESPACE) {
//                        break;
//                    }
//
//                    $phpcsFile->fixer->replaceToken($i, '');
//                }
//
//                for ($i = ($arrayEnd - 1); $i > $arrayStart; $i--) {
//                    if ($tokens[$i]['code'] !== T_WHITESPACE) {
//                        break;
//                    }
//
//                    $phpcsFile->fixer->replaceToken($i, '');
//                }
//
//                $phpcsFile->fixer->endChangeset();
//            }

//            return;
//        }//end if

        /*
            This section checks for arrays that don't specify keys.

            Arrays such as:
               array(
                'aaa',
                'bbb',
                'd',
               );
        */
        //Ongr Multi-line array is not empty and nothing but 1 value was found.
        if (empty($indices)) {
            $value = $arrayStart + 1;
            while ($tokens[$value]['code'] == T_WHITESPACE) {
                $value++;
            }
            $error = 'Each line in an array declaration must end with a comma';
            $phpcsFile->addError($error, $value, 'NoComma');
        }

        if ($keyUsed === false && empty($indices) === false) {
            $count     = count($indices);
            $lastIndex = $indices[($count - 1)]['value'];

            $trailingContent = $phpcsFile->findPrevious(
                PHP_CodeSniffer_Tokens::$emptyTokens,
                ($arrayEnd - 1),
                $lastIndex,
                true
            );

            if ($tokens[$trailingContent]['code'] !== T_COMMA) {
                $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
                $error = 'Comma required after last value in array declaration';
                $fix   = $phpcsFile->addFixableError($error, $trailingContent, 'NoCommaAfterLast');
                if ($fix === true) {
                    $phpcsFile->fixer->addContent($trailingContent, ',');
                }
            } else {
                $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
            }

            $lastValueLine = false;
            foreach ($indices as $value) {
                if (empty($value['value']) === true) {
                    // Array was malformed and we couldn't figure out
                    // the array value correctly, so we have to ignore it.
                    // Other parts of this sniff will correct the error.
                    continue;
                }
                //Ongr we alowe this.
//                if ($lastValueLine !== false && $tokens[$value['value']]['line'] === $lastValueLine) {
//                    $error = 'Each value in a multi-line array must be on a new line';
//                    $fix   = $phpcsFile->addFixableError($error, $value['value'], 'ValueNoNewline');
//                    if ($fix === true) {
//                        if ($tokens[($value['value'] - 1)]['code'] === T_WHITESPACE) {
//                            $phpcsFile->fixer->replaceToken(($value['value'] - 1), '');
//                        }
//
//                        $phpcsFile->fixer->addNewlineBefore($value['value']);
//                    }
//                } else
                if ($tokens[($value['value'] - 1)]['code'] === T_WHITESPACE) {
                    $expected = $statementStart + 4;

                    $first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $value['value'], true);
                    $found = $tokens[$value['value']]['column'];
                    if ($found !== $expected) {
                        $error = 'Array value not aligned correctly; expected %s spaces but found %s';
                        $data  = array(
                            $expected,
                            $found,
                        );

                        $phpcsFile->addError($error, $value['value'], 'ValueNotAligned', $data);
//                        $fix = $phpcsFile->addFixableError($error, $value['value'], 'ValueNotAligned', $data);
//                        if ($fix === true) {
//                            if ($found === 0) {
//                                $phpcsFile->fixer->addContent(($value['value'] - 1), str_repeat(' ', $expected));
//                            } else {
//                                $phpcsFile->fixer->replaceToken(($value['value'] - 1), str_repeat(' ', $expected));
//                            }
//                        }
                    }
                }//end if

                $lastValueLine = $tokens[$value['value']]['line'];
            }//end foreach
        }//end if

        /*
            Below the actual indentation of the array is checked.
            Errors will be thrown when a key is not aligned, when
            a double arrow is not aligned, and when a value is not
            aligned correctly.
            If an error is found in one of the above areas, then errors
            are not reported for the rest of the line to avoid reporting
            spaces and columns incorrectly. Often fixing the first
            problem will fix the other 2 anyway.

            For example:

            $a = array(
                  'index'  => '2',
                 );

            or

            $a = [
                  'index'  => '2',
                 ];

            In this array, the double arrow is indented too far, but this
            will also cause an error in the value's alignment. If the arrow were
            to be moved back one space however, then both errors would be fixed.
        */

        $numValues = count($indices);
        //Ongr we use our own method to find statement start comment.
        $indicesStart  = $statementStart + 4;
        $arrowStart    = ($indicesStart + $maxLength + 1);
        $valueStart    = ($arrowStart + 3);
        $indexLine     = $tokens[$stackPtr]['line'];
        $lastIndexLine = null;
        foreach ($indices as $index) {
            if (isset($index['index']) === false) {
                // Array value only.
                if (($tokens[$index['value']]['line'] === $tokens[$stackPtr]['line'])) {
                    $error = 'The first value in a multi-value array must be on a new line';
                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'FirstValueNoNewline');
                    if ($fix === true) {
                        $phpcsFile->fixer->addNewlineBefore($index['value']);
                    }
                }

                continue;
            }

            $lastIndexLine = $indexLine;
            $indexLine     = $tokens[$index['index']]['line'];

            if ($indexLine === $tokens[$stackPtr]['line']) {
                $error = 'The first index in a multi-value array must be on a new line';
                $fix   = $phpcsFile->addFixableError($error, $index['index'], 'FirstIndexNoNewline');
                if ($fix === true) {
                    $phpcsFile->fixer->addNewlineBefore($index['index']);
                }

                continue;
            }

            if ($indexLine === $lastIndexLine) {
                $error = 'Each index in a multi-line array must be on a new line';
                $fix   = $phpcsFile->addFixableError($error, $index['index'], 'IndexNoNewline');
                if ($fix === true) {
                    if ($tokens[($index['index'] - 1)]['code'] === T_WHITESPACE) {
                        $phpcsFile->fixer->replaceToken(($index['index'] - 1), '');
                    }

                    $phpcsFile->fixer->addNewlineBefore($index['index']);
                }

                continue;
            }
            if ($tokens[$index['index']]['column'] !== $indicesStart) {
                $expected = ($indicesStart - 1);
                $found    = ($tokens[$index['index']]['column'] - 1);
                $error    = 'Array key not aligned correctly; expected %s spaces but found %s';
                $data     = array(
                    $expected,
                    $found,
                );

                $phpcsFile->addError($error, $index['index'], 'KeyNotAligned', $data);
//                $fix = $phpcsFile->addFixableError($error, $index['index'], 'KeyNotAligned', $data);
//                if ($fix === true) {
//                    if ($found === 0) {
//                        $phpcsFile->fixer->addContent(($index['index'] - 1), str_repeat(' ', $expected));
//                    } else {
//                        $phpcsFile->fixer->replaceToken(($index['index'] - 1), str_repeat(' ', $expected));
//                    }
//                }

                continue;
            }
//Ongr start
//            if ($tokens[$index['arrow']]['column'] !== $arrowStart) {
//                $expected = ($arrowStart - (strlen($index['index_content']) + $tokens[$index['index']]['column']));
//                $found    = ($tokens[$index['arrow']]['column'] - (strlen($index['index_content']) + $tokens[$index['index']]['column']));
//                $error    = 'Array double arrow not aligned correctly; expected %s space(s) but found %s';
//                $data     = array(
//                    $expected,
//                    $found,
//                );
//
//                $fix = $phpcsFile->addFixableError($error, $index['arrow'], 'DoubleArrowNotAligned', $data);
//                if ($fix === true) {
//                    if ($found === 0) {
//                        $phpcsFile->fixer->addContent(($index['arrow'] - 1), str_repeat(' ', $expected));
//                    } else {
//                        $phpcsFile->fixer->replaceToken(($index['arrow'] - 1), str_repeat(' ', $expected));
//                    }
//                }
//
//                continue;
//            }
//
//            if ($tokens[$index['value']]['column'] !== $valueStart) {
//                $expected = ($valueStart - ($tokens[$index['arrow']]['length'] + $tokens[$index['arrow']]['column']));
//                $found    = ($tokens[$index['value']]['column'] - ($tokens[$index['arrow']]['length'] + $tokens[$index['arrow']]['column']));
//                if ($found < 0) {
//                    $found = 'newline';
//                }
//
//                $error = 'Array value not aligned correctly; expected %s space(s) but found %s';
//                $data  = array(
//                    $expected,
//                    $found,
//                );
//
//                $fix = $phpcsFile->addFixableError($error, $index['arrow'], 'ValueNotAligned', $data);
//                if ($fix === true) {
//                    if ($found === 'newline') {
//                        $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($index['value'] - 1), null, true);
//                        $phpcsFile->fixer->beginChangeset();
//                        for ($i = ($prev + 1); $i < $index['value']; $i++) {
//                            $phpcsFile->fixer->replaceToken($i, '');
//                        }
//
//                        $phpcsFile->fixer->replaceToken(($index['value'] - 1), str_repeat(' ', $expected));
//                        $phpcsFile->fixer->endChangeset();
//                    } else if ($found === 0) {
//                        $phpcsFile->fixer->addContent(($index['value'] - 1), str_repeat(' ', $expected));
//                    } else {
//                        $phpcsFile->fixer->replaceToken(($index['value'] - 1), str_repeat(' ', $expected));
//                    }
//                }
//            }//end if
//ongr end
            // Check each line ends in a comma.
//            if ($tokens[$index['value']]['code'] !== T_ARRAY
//                && $tokens[$index['value']]['code'] !== T_OPEN_SHORT_ARRAY
//            ) {
            $valueLine = $tokens[$index['value']]['line'];
            $nextComma = false;
            for ($i = ($index['value'] + 1); $i < $arrayEnd; $i++) {
                // Skip bracketed statements, like function calls.
                if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
                    $i         = $tokens[$i]['parenthesis_closer'];
                    $valueLine = $tokens[$i]['line'];
                    continue;
                }

                if ($tokens[$i]['code'] === T_COMMA) {
                    $nextComma = $i;
                    break;
                }
            }

            if (($nextComma === false) || ($tokens[$nextComma]['line'] !== $valueLine) && $tokens[$index['value']]['code'] !== T_OPEN_SHORT_ARRAY) {
                $error = 'Each line in an array declaration must end in a comma';
                $fix   = $phpcsFile->addFixableError($error, $index['value'], 'NoComma');

                if ($fix === true) {
                    // Find the end of the line and put a comma there.
                    for ($i = ($index['value'] + 1); $i < $phpcsFile->numTokens; $i++) {
                        if ($tokens[$i]['line'] > $tokens[$index['value']]['line']) {
                            break;
                        }
                    }

                    $phpcsFile->fixer->addContentBefore(($i - 1), ',');
                }
            }

            // Check that there is no space before the comma.
            if ($nextComma !== false && $tokens[($nextComma - 1)]['code'] === T_WHITESPACE) {
                $content     = $tokens[($nextComma - 2)]['content'];
                $spaceLength = $tokens[($nextComma - 1)]['length'];
                $error       = 'Expected 0 spaces between "%s" and comma; %s found';
                $data        = array(
                    $content,
                    $spaceLength,
                );

                $fix = $phpcsFile->addFixableError($error, $nextComma, 'SpaceBeforeComma', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->replaceToken(($nextComma - 1), '');
                }
            }
//            }//end if
        }//end foreach

    }//end processMultiLineArray()
    /**
     * @param PHP_CodeSniffer_File $phpcsFile
     * @param int                  $stackPtr
     *
     * @return mixed
     */
    private function getStatementStartColumn(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();
        $line = $tokens[$stackPtr]['line'];
        do {
            $stackPtr--;
        } while ($tokens[$stackPtr]['line'] == $line);
        do {
            $stackPtr++;
        } while ($tokens[$stackPtr]['code'] == T_WHITESPACE);
        return $tokens[$stackPtr]['column'];
    }

}//end class


<?php

/*
 * This file is part of the Plum package.
 *
 * (c) 2010-2012 Julien Brochet <mewt@madalynn.eu>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Plum\Deployer;

use Plum\Server\ServerInterface;
use Plum\Exception\SshException;

class SshDeployer extends AbstractDeployer
{
    /**
     * The SSH connection
     *
     * @var ressource
     */
    protected $con;

    /**
     * {@inheritDoc}
     */
    public function doDeploy(ServerInterface $server, array $options, $dryRun)
    {
        $commands = isset($options['commands']) ? $options['commands'] : array();
        if (0 === count($commands)) {
            // The SSH deployer is useless if the user has no command
            return;
        }

        if (null === $server->getPassword()) {
            throw new SshException('No password found for the server.');
        }

        $this->connect($server);

        if (false === $dryRun) {
            foreach ($commands as $command) {
                // We need to jump to the right directory..
                $command = sprintf('cd %s && %s', $server->getDir(), $command);
                $this->exec($command);
            }
        }

        $this->disconnect();
    }

    /**
     * Open the SSH connection
     *
     * @param Plum\Server\ServerInterface $server
     */
    protected function connect(ServerInterface $server)
    {
        if (false === function_exists('ssh2_connect')) {
            throw new \RuntimeException('The "ssh2_connect" function does not exist.');
        }

        $con = ssh2_connect($server->getHost(), $server->getPort());

        if (false === $con) {
            throw new SshException(sprintf('Cannot connect to server "%s"', $server->getHost()));
        }

        if (false === ssh2_auth_password($con, $server->getUser(), $server->getPassword())) {
            throw new SshException(sprintf('Authorization failed for user "%s"', $server->getUser()));
        }

        $this->con = $con;
    }

    /**
     * Close the SSH connection
     */
    protected function disconnect()
    {
        $this->exec('echo "EXITING" && exit;');
    }

    /**
     * Execute a SSH command
     *
     * @param string $cmd The SSH command
     *
     * @return string The output
     */
    protected function exec($cmd)
    {
        if (false === $stream = ssh2_exec($this->con, $cmd)) {
            throw new SshException(sprintf('"%s" : SSH command failed', $cmd));
        }

        stream_set_blocking($stream, true);

        $data = '';
        while ($buf = fread($stream, 4096)) {
            $data .= $buf;
        }

        fclose($stream);

        return $data;
    }

    /**
     * {@inheritDoc}
     */
    public function getName()
    {
        return 'ssh';
    }
}


<?php

namespace CarbonPHP;

use CarbonPHP\Helpers\Serialized;
use CarbonPHP\programs\CLI;

/**
 * Class Carbon
 * @package Carbon
 *
 * This is CarbonPHP, a simple framework designed to make building robust web
 * applications extremely quick. The following class is the setup for the rest
 * of library. For reference and support visit
 *
 * @link http://www.carbonphp.com/
 */
class CarbonPHP
{
    /**
     * @var bool $safelyExit determines if start application should be executed
     * when running the invoke magic method.
     */
    private $safelyExit = false;

    public static $setupComplete = false;

    /** If safely exit is false run startApplication(), otherwise return $safelyExit
     * @link http://php.net/manual/en/language.oop5.magic.php#object.invoke
     * @param string $application The class to execute. This must extend CarbonPHP/Application
     * @return bool
     */
    public function __invoke($application)
    {
        return $this->safelyExit ?: startApplication($application);
    }

    /**
     * @type $PHP = [
     *       'AUTOLOAD' => string array []                       // Provide PSR-4 namespace roots
     *       'SITE' => [
     *           'URL' => string '',                                  // Server Url name you do not need to chane in remote development
     *           'ROOT' => string '__FILE__',                         // This was defined in our ../index.php
     *           'CACHE_CONTROL' => [                                 // Key value map of $extension => $headers
     * 'png|jpg|gif|jpeg|bmp|icon|js|css' => 'Cache-Control: max-age=<seconds>',
     * 'woff|woff2|map|hbs|eotv' => 'Cache-Control: no-cache ',              // if the extension is found the headers provided will be sent
     * ],
     *           'CONFIG' => string __FILE__,                         // Send to sockets
     *           'TIMEZONE' => string 'America/Chicago',              // Current timezone TODO - look up php
     *           'TITLE' => string 'Carbon 6',                        // Website title
     *           'VERSION' => string /phpversion(),                   // Add link to semantic versioning
     *           'SEND_EMAIL' => string '',                           // I send emails to validate accounts
     *           'REPLY_EMAIL' => string '',
     *           'BOOTSTRAP' => string '',                            // This file is executed when the startApplication() function is called
     *           'HTTP' => bool true
     *       ],
     *       'DATABASE' => [
     *           'DB_DSN'  => string '',                        // Host and Database get put here
     *           'DB_USER' => string '',
     *           'DB_PASS' => string '',
     *           'DB_BUILD'=> string '',                        // Absolute file path to your database set up file
     *           'REBUILD' => bool false
     *       ],
     *       'SESSION' => [
     *           'REMOTE' => bool true,             // Store the session in the SQL database
     *           'SERIALIZE' => [],                 // These global variables will be stored between session
     *           'CALLBACK' => callable,
     *       'SOCKET' => [
     *           'HOST' => string '',               // The IP or DNS server to connect ws or wss with
     *           'WEBSOCKETD' => bool false,        // Todo - remove websockets
     *           'PORT' => int 8888,
     *           'DEV' => bool false,
     *           'SSL' => [
     *               'KEY' => string '',
     *               'CERT' => string ''
     *           ]
     *       ],
     *       'ERROR' => [
     *           'LEVEL' => (int) E_ALL | E_STRICT,
     *           'STORE' => (bool) true,                // Database if specified and / or File 'LOCATION' in your system
     *           'SHOW' => (bool) true,                 // Show errors on browser
     *           'FULL' => (bool) true                  // Generate custom stacktrace will high detail - DO NOT set to TRUE in PRODUCTION
     *       ],
     *       'VIEW' => [
     *           'VIEW' => string '/',          // This is where the MVC() function will map the HTML.PHP and HTML.HBS . See Carbonphp.com/mvc
     *          'WRAPPER' => string '',         // View::content() will produce this
     *      ],
     * ]
     * @throws \Exception
     */
    public function __construct(string $PHP = null)
    {
        // TODO - make a cache of these consts

        ####################  Sockets will have already claimed this global
        \defined('TEST') OR \define('TEST', $_ENV['TEST'] ?? false);

        if (TEST) {
            $this->safelyExit = true;  // We just want the env to load, not route life :)
            $_SERVER = [
                'REMOTE_ADDR' => '::1',
                'REMOTE_PORT' => '53950',
                'SERVER_SOFTWARE' => 'PHP 7.2.3 Development Server',
                'SERVER_PROTOCOL' => 'HTTP/1.1',
                'SERVER_NAME' => 'localhost',
                'SERVER_PORT' => '80',
                'REQUEST_URI' => '/login/',
                'REQUEST_METHOD' => 'GET',
                'SCRIPT_NAME' => '/index.php',
                'SCRIPT_FILENAME' => "C:\Users\rmiles\Documents\GitHub\Stats.Coach\index.php",
                'PATH_INFO' => '/login/',
                'PHP_SELF' => '/index.php/login/',
                'HTTP_HOST' => 'localhost:88',
                'HTTP_CONNECTION' => 'keep-alive',
                'HTTP_CACHE_CONTROL' => 'max-age=0',
                'HTTP_UPGRADE_INSECURE_REQUESTS' => '1',
                'HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
                'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                'HTTP_REFERER' => 'http://localhost:88/',
                'HTTP_ACCEPT_ENCODING' => 'gzip, deflate, br',
                'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.9',
                'HTTP_COOKIE' => 'PHPSESSID=gn4amaq3el5giekaboa29q27gp; _gid=GA1.1.1938536140.1530039320',
                'REQUEST_TIME_FLOAT' => 1530054388.652,
                'REQUEST_TIME' => 1530054388,
            ];
        }
        ####################  Sockets will have already claimed this global
        \defined('SOCKET') OR \define('SOCKET', false);

        ####################  Define your own server root
        \defined('APP_ROOT') OR \define('APP_ROOT', CARBON_ROOT);

        ####################  For help loading our Carbon.js
        \defined('CARBON_ROOT') OR \define('CARBON_ROOT', __DIR__ . DS);

        ####################  Did we use >> php -S localhost:8080 index.php
        \defined('APP_LOCAL') OR \define('APP_LOCAL', $this->isClientServer());

        ####################  May as well make composer a dependency
        \defined('COMPOSER_ROOT') OR \define('COMPOSER_ROOT', \dirname(CARBON_ROOT, 2) . DS);

        ####################  Template Root
        \defined('TEMPLATE_ROOT') OR \define('TEMPLATE_ROOT', CARBON_ROOT);

        ################  Helpful Global Functions ####################
        if (!file_exists(CARBON_ROOT . 'helpers' . DS . 'Application.php') || !include CARBON_ROOT . 'helpers' . DS . 'Application.php') {
            print '
Your instance of CarbonPHP appears corrupt. Please see CarbonPHP.com for Documentation.
';
            die(1);
        }

        ####################  Now load config file so globals above & stacktrace security
        if ($PHP !== null) {
            if (file_exists($PHP)) {
                $PHP = include $PHP;            // this file must return an array!
            } elseif ($PHP !== null) {
                print 'Invalid configuration path given! ' . $PHP;
                $this->safelyExit = true;
                return;
            }
        }

        #######################   VIEW      ######################
        \define('APP_VIEW', $PHP['VIEW']['VIEW'] ?? DS);         // Public Folder

        View::$wrapper = APP_ROOT . APP_VIEW . $PHP['VIEW']['WRAPPER'] ?? '';

        ####################  GENERAL CONF  ######################
        error_reporting($PHP['ERROR']['LEVEL'] ?? E_ALL | E_STRICT);

        ini_set('display_errors', $PHP['ERROR']['SHOW'] ?? true);

        date_default_timezone_set($PHP['SITE']['TIMEZONE'] ?? 'America/Chicago');

        \defined('DS') OR \define('DS', DIRECTORY_SEPARATOR);

        \defined('APP_ROOT') OR \define('APP_ROOT', CARBON_ROOT);

        \define('REPORTS', $PHP['ERROR']['LOCATION'] ?? APP_ROOT);

        #####################   AUTOLOAD    #######################
        if ($PHP['AUTOLOAD'] ?? false) {
            $PSR4 = include CARBON_ROOT . 'AutoLoad.php';
            if (\is_array($PHP['AUTOLOAD'] ?? false)) {
                foreach ($PHP['AUTOLOAD'] as $name => $path) {
                    $PSR4->addNamespace($name, $path);
                }
            }
        }

        #####################   ERRORS    #######################
        /**
         * TODO - debating on removing the start and attempting to catch our own errors. look into later
         * So I've looked into it and discovered thrown errors can return to the current execution point
         * We must now decide how and when we throw errors / exceptions..
         *
         * Questions still to test. When does the error catcher get resorted to?
         * Do Try Catch block have a higher precedence than the error catcher?
         * What if that error is thrown multiple function levels down in a block?
         **/

        if ($PHP['ERROR'] ?? false) {
            Error\ErrorCatcher::$defaultLocation = REPORTS . 'Log_' . ($_SESSION['id'] ?? '') . '_' . time() . '.log';
            Error\ErrorCatcher::$fullReports = $PHP['ERROR']['STORE'] ?? false;
            Error\ErrorCatcher::$printToScreen = $PHP['ERROR']['SHOW'] ?? true;
            Error\ErrorCatcher::$storeReport = $PHP['ERROR']['FULL'] ?? true;
            Error\ErrorCatcher::$level = $PHP['ERROR']['LEVEL'] ?? ' E_ALL | E_STRICT';
            Error\ErrorCatcher::start();
        } // Catch application errors and alerts

        #################  DATABASE  ########################
        if ($PHP['DATABASE'] ?? false) {
            Database::$dsn = 'mysql:host='.($PHP['DATABASE']['DB_HOST'] ?? '').';dbname='. ($PHP['DATABASE']['DB_NAME'] ?? '') .';';
            Database::$username = $PHP['DATABASE']['DB_USER'] ?? '';
            Database::$password = $PHP['DATABASE']['DB_PASS'] ?? '';
            Database::$setup = $PHP['DATABASE']['DB_BUILD'] ?? '';
        }

        #################  SITE  ########################
        if ($PHP['SITE'] ?? false) {
            \define('SITE_TITLE', $PHP['SITE']['TITLE'] ?? 'CarbonPHP');                     // Carbon doesnt use
            \define('SITE_VERSION', $PHP['SITE']['VERSION'] ?? PHP_VERSION);                // printed in the footer
            \define('SYSTEM_EMAIL', $PHP['SEND_EMAIL'] ?? '');                               // server email system
            \define('REPLY_EMAIL', $PHP['REPLY_EMAIL'] ?? '');                               // I give you options :P
        }

        // TODO - move to app invocation
        // PHPUnit Runs in a cli to ini the 'CarbonPHP' env.
        // We're not testing out extra resources
        if (PHP_SAPI === 'cli' && !TEST && !SOCKET) {
            $this->safelyExit = true;
            $cli = new CLI($PHP);
            $cli->run($_SERVER['argv'] ?? ['index.php', null]);
            $cli->cleanUp($PHP);
            return;
        }

        ##################  VALIDATE URL / URI ##################
        // This is the first step that could kick users out of our application.
        // Even if a request is bad, we need to store the log
        if (!\defined('IP')) {
            $this->IP_FILTER();
        }

        // This is the first event that could resolve the request (to a file), respond, and exit safely
        $this->URI_FILTER($PHP['SITE']['URL'] ?? '', $PHP['SITE']['CACHE_CONTROL'] ?? []);

        // TODO - I'm probably going to move this to the cli
        if ($PHP['DATABASE']['REBUILD'] ?? false) {
            Database::setUp(false);   // redirect = false
            $this->safelyExit = true;
            return;
        }

        #######################   Pjax Ajax Refresh  ######################
        // Must return a non empty value
        \define('PJAX', SOCKET ? false : isset($_GET['_pjax']) || (isset($_SERVER['HTTP_X_PJAX']) && $_SERVER['HTTP_X_PJAX']));

        if (PJAX && empty($_POST)) {
            # try to json decode. Json payloads ar sent to the input stream
            $_POST = json_decode(file_get_contents('php://input'), true);
        }

        // (PJAX == true) return required, else (!PJAX && AJAX) return optional (socket valid)
        \define('AJAX', SOCKET ? false : PJAX || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'));

        \define('HTTPS', SOCKET ? false : ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? false) === 'https' || ($_SERVER['HTTPS'] ?? false) !== 'off');

        \define('HTTP', !(HTTPS || SOCKET || AJAX));

        // PHPUnit testing should not exit on explicit http(s) requests
        if (!TEST && HTTP && !($PHP['SITE']['HTTP'] ?? true)) {
            if (headers_sent()) {
                print '
Failed to switch to https, headers already sent! Please contact the server administrator.
';
            } else {
                header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
            }
            die(1);
        }

        // TODO - I think we should make this optional
        #AJAX OR $_POST = []; // We only allow post requests through ajax/pjax

        ########################  Session Management ######################
        if ($PHP['SESSION'] ?? true) {
            if ($PHP['SESSION']['PATH'] ?? false) {
                session_save_path($PHP['SESSION']['PATH'] ?? '');   // Manually Set where the Users Session Data is stored
            }

            new Session(IP, $PHP['SESSION']['REMOTE'] ?? false); // session start

            $_SESSION['id'] = array_key_exists('id', $_SESSION ?? []) ? $_SESSION['id'] : false;

            if (\is_callable($PHP['SESSION']['CALLBACK'] ?? null)) {
                Session::updateCallback($PHP['SESSION']['CALLBACK']); // Pull From Database, manage socket ip
            }
        }

        if (\is_array($PHP['SESSION']['SERIALIZE'] ?? false)) {
            forward_static_call_array([Serialized::class, 'start'], $PHP['SESSION']['SERIALIZE']);    // Pull theses from session, and store on shutdown
        }

        self::$setupComplete = true;
    }

    /**
     * returns 127.0.0.1 if you are using a local development server,
     * otherwise returns false.
     * @return bool|mixed|string
     */
    private function isClientServer()
    {
        if (PHP_SAPI === 'cli-server' || PHP_SAPI === 'cli' || \in_array($_SERVER['REMOTE_ADDR'] ?? [], ['127.0.0.1', 'fe80::1', '::1'], false)) {
            if (SOCKET && $ip = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                \define('IP', $ip);
                return false;
            }
            \define('IP', '127.0.0.1');
            return IP;
        }
        return false;
    }

    /** If a url is encoded in a version control View::versionControl()
     * or ends in a file extension attempt to load it and send with
     * appropriate headers.
     * @param string $URL by the user configuration file.
     * If the url is not equal to the server url, and we are not
     * on a local development server, then redirect to url provided.
     *
     *
     * @param array|null $cacheControl
     * @return bool
     */
    private
    function URI_FILTER(string $URL = 'CarbonPHP.com', array $cacheControl = null): bool
    {
        if (!empty($URL = strtolower($URL)) && $_SERVER['SERVER_NAME'] !== $URL && !APP_LOCAL) {
            header("Refresh:0; url=$URL");
            print '
    <meta http-equiv="refresh" content="5; url=' . $URL . '">' .
                IP . '
You appear to be lost.

Moving to  ' . $URL . '
' .
                "";
            $this->safelyExit = true;
        }

        \define('URI', trim(urldecode(parse_url(trim(preg_replace('/\s+/', ' ', $_SERVER['REQUEST_URI'])), PHP_URL_PATH)), '/'), true);

        \define('URL',
            (isset($_SERVER['SERVER_NAME']) ?
                ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] === 443 ? 'https://' : 'http://') .
                $_SERVER['SERVER_NAME'] . (APP_LOCAL ? ':' . $_SERVER['SERVER_PORT'] : '') : null), true);

        \define('SITE', url . '/', true);   // http(s)://example.com/  - URL's require a forward slash so DS may not work on os (windows)

        // It does not matter if this matches, we will take care of that in the next if.

        $allowedEXT = implode('|', array_keys($cacheControl));

        preg_match("#^(.*\.)($allowedEXT)\?*.*#", URI, $matches, PREG_OFFSET_CAPTURE);

        // So if the request has an extension that's not allowed we ignore it and keep processing as a valid route
        $ext = $matches[2][0] ?? '';    // routes should be null

        if (empty($ext)) {              // We're requesting a file
            return true;
        }

        // we need to ensure valid access
        $allowedAccess = false;
        foreach ($cacheControl as $extension => $headers) {
            if (strpos($extension, $ext) !== false) {
                $allowedAccess = true;
                header($headers);
                break;
            }
        }

        if (!$allowedAccess) {
            http_response_code(403);            // This is a Forbidden response
            exit(1);                                        // This is an exit with error
        }

        // Look for versioning
        View::unVersion($_SERVER['REQUEST_URI']);           // This may exit and send a file

        // add cache control

        if (file_exists(COMPOSER_ROOT . URI)) {             // Composer is now always the base uri
            View::sendResource(COMPOSER_ROOT . URI, $ext);
        }

        // Not versioned, so see it it exists
        if (file_exists(APP_ROOT . URI)) {      //  also may send and exit
            View::sendResource(URI, $ext);
        }
        http_response_code(404);           // If we haven't found the request send code 404 not found
        die(1);
    }

    /** This function uses common keys for obtaining the users real IP.
     * We use this for verbo se operating systems support.
     * @link http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
     * @return mixed|string
     */
    private function IP_FILTER()
    {
        $ip_keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR');
        foreach ($ip_keys as $key) {
            if (array_key_exists($key, $_SERVER) === true) {
                foreach (explode(',', $_SERVER[$key]) as $ip) {
                    // trim for safety measures
                    $ip = trim($ip);
                    if ($ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                        \define('IP', $ip);
                        return IP;
                    }
                }
            }
        }   // TODO - log invalid ip addresses
        print 'Could not establish an IP address.';
        die(1);
    }

}


<?php

namespace CarbonPHP;

/**
 * Class View
 * @package Carbon
 */
class View
{
    /**
     * @var string
     */
    public static $bufferedContent = 'No Content Buffered';
    /**
     * @var bool
     */
    public static $forceWrapper = false;
    /**
     * @var
     */
    public static $wrapper;

    /**
     * @param string $file
     * @return bool
     * @throws \Mustache_Exception_InvalidArgumentException
     */
    public static function content(string $file): bool
    {
        global $json;

        $buffer = function () use ($file) : string {         // closure  $buffer();

            global $json;              // Buffer contents may not need to be run if AJAX or SOCKET

            ob_start();                 // closure of a buffer is kinda like a double buffer

            if (($json['alert'] ?? false) && \is_array($json['alert']) && !empty($json['alert'])) {
                foreach ($json['alert'] as $level => $stack) {
                    foreach ($stack as $item => $message) {
                        self::bootstrapAlert($message, $level);
                    }
                }   // If a public alert is set it will be processed here.
                $alert = null;
            }

            if (!file_exists($file) && !file_exists($file = APP_ROOT . $file)) {
                // It was already handled if it was an hbs, but lets remind people that both are supported
                self::bootstrapAlert("The file ($file.(hbs|php))requested could not be found.", 'danger');
            } else if (!SOCKET) {
                include $file;          // TODO - remove socket check?
            } else {
                $json = array_merge([
                    'Mustache' => SITE . $file,
                    'Widget' => '#pjax-content'
                ], $json);
                //print_r($json);
                print PHP_EOL;
            }
            return ob_get_clean();
        };

        if (SOCKET) {
            print $buffer() . PHP_EOL;
            return true;
        }

        if (pathinfo($file, PATHINFO_EXTENSION) === 'hbs') {
            $mustache = new \Mustache_Engine();

            if (SOCKET || (!self::$forceWrapper && PJAX && AJAX)) {        // Send JSON To Socket

                $json = array_merge([
                    'Mustache' => SITE . $file,
                    'Widget' => '#pjax-content'
                ], $json);

                headers_sent() or header('Content-Type: application/json');

                print json_encode($json) . PHP_EOL . PHP_EOL;

                return true;
            }

            $buffer = $mustache->render($buffer(), $json);                  // Render Inner Content
        } else {
            $buffer = $buffer();
        }

        // Make sure our buffer didn't fail

        if (!file_exists(self::$wrapper)) {
            print '
The content wrapper (' . self::$wrapper . ') was not found.
';
            $message = fopen(__FILE__, 'rb');
            // Go to the end of the __halt_compiler();
            fseek($message, __COMPILER_HALT_OFFSET__);
            print stream_get_contents($message);
            fclose($message);
            return false;
        }

        if (!\is_string($buffer)) {
            $buffer = "";
        }

        if (!self::$forceWrapper && (PJAX || AJAX)):        // Send only inner content?
            print $buffer;

        #################### Send the Outer Wrapper
        elseif (pathinfo(self::$wrapper, PATHINFO_EXTENSION) === 'hbs'):   // Outer Wrapper is Mustache
            $json['content'] = $buffer;
            $mustache = $mustache ?? new \Mustache_Engine();
            print $mustache->render(file_get_contents(self::$wrapper), $json);
        else:                                                                       // Outer Wrapper is PHP?
            self::$bufferedContent = $buffer;
            /** @noinspection PhpIncludeInspection */
            include_once self::$wrapper;
        endif;

        return true;    // This should fall, or pop, on the stack to the bootstrap which will return because of a match, then to the index.php
    }

    /** This method
     * @param $message
     * @param $level
     */
    public static function bootstrapAlert($message, $level): void
    {
        $message = htmlentities($message);  // Im not sure how this may be used,
        $level = htmlentities($level);      // for completeness I sanitises
        printf('', $message, $level);
    }

    /**
     *  Given a file, i.e. /css/base.css, replaces it with a string containing the
     *  file's mtime, i.e. /css/base.1221534296.css.
     *
     * @param $file
     *  file to be loaded.  Must be an absolute path (i.e.
     *                starting with slash).
     * @return mixed  file to be loaded.
     */

    public static function versionControl($file)
    {
        if (!\defined('SERVER_ROOT')) {
            return DS . $file;
        }
        try {
            if (!file_exists($absolute = APP_ROOT . $file) || !($time = @filemtime($absolute))) {
                return DS . $file;
            }
            return preg_replace('{\\.([^./]+)$}', '.' . $time . '.\$1', DS . $file);
        } catch (\ErrorException $e) {
            return DS . $file;
        }
    }

    /**
     *  Given a file versioned by file's mtime, i.e. /css/base.1221534296.css.
     *  return the requested file with proper headers, i.e. /css/base.css.
     *
     * @param $uri
     *  file to be loaded.  Must be an absolute path (i.e.
     *                starting with slash).
     * @return mixed  file to be loaded.
     */
    public static function unVersion($uri)
    {
        if (!\defined('SERVER_ROOT')) {
            return DS . $uri;
        }
        if (preg_match('#^(.*)\.[\d]{10}\.(css|js)#', $uri, $matches, PREG_OFFSET_CAPTURE)) {

            $uri = trim($matches[1][0] . '.' . $matches[2][0], '/');

            if (file_exists(APP_ROOT . $uri)) {
                self::sendResource($uri, $matches[2][0]);
            }
            if (file_exists(TEMPLATE_ROOT . $uri)) {
                self::sendResource(TEMPLATE_ROOT . $uri, $matches[2][0]);
            }
            if (file_exists(COMPOSER_ROOT . $uri)) {
                self::sendResource(COMPOSER_ROOT . $uri, $matches[2][0]);
            }
        }
        return false;
    }

    /**
     * @param $file
     * @param $ext
     */
    public static function sendResource($file, $ext): void
    {

        if ($mime = self::mimeType($file, $ext)) {
            header('Content-type:' . $mime . '; charset: UTF-8');
            readfile($file);
            exit(1);                        // exit = die  but implies success
        }
        http_response_code(404);
        die(1);
    }

    //Josh Sean
    public static function generateUpToDateMimeArray(): void
    {
        \defined('APACHE_MIME_TYPES_URL') OR \define('APACHE_MIME_TYPES_URL', 'http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types');
        $s = array();
        foreach (@explode("\n", @file_get_contents(APACHE_MIME_TYPES_URL)) as $x) {
            if (isset($x[0]) && $x[0] !== '#' && preg_match_all('#([^\s]+)#', $x, $out) && isset($out[1]) && ($c = count($out[1])) > 1) {
                for ($i = 1; $i < $c; $i++) {
                    $s[] = '   \'' . $out[1][$i] . '\' => \'' . $out[1][0] . '\'';
                }
            }
        }
        print @sort($s) ? 'return array(
' . implode($s, ',
') . '
);' : false;
    }

    /**
     * @param $file
     * @param $ext
     * @return string
     */
    public static function mimeType($file, $ext): string
    {
        #self::generateUpToDateMimeArray() and die;

        $mime_types = include CARBON_ROOT . 'Extras/mimeTypes.php';

        if (array_key_exists($ext, $mime_types)) {
            return $mime_types[$ext];
        }

        if (\function_exists('finfo_open')) {
            $file_info = finfo_open(FILEINFO_MIME);
            $mime_types = finfo_file($file_info, $file);
            finfo_close($file_info);
            return $mime_types;
        }
        return 'application/octet-stream';
    }

}

__halt_compiler();

Wrapper does not exist


<?php
/**
 * Created by IntelliJ IDEA.
 * User: Miles
 * Date: 8/23/17
 * Time: 11:00 AM
 */

namespace CarbonPHP\Helpers;

use CarbonPHP\Error\ErrorCatcher;

/**
 * Class Pipe
 * @package Carbon\Helpers
 *
 * In both method in this class I have commented out changing the
 * user / owner of the named pipe. Some environments MAY require that
 * you change permissions. Over the past few years I've migrated to
 * many diffrent hosting solutions and I believe this
 * was a fix on one. But im not positive...
 */
class Pipe
{
    /** This will open a named pipe on our server. This is used for sending
     * information between two active processes on the server. Generally,
     * each user can send data real time to each other using this method.
     * Keep in mind that the fifo file must be unique to the users listening
     * on it.
     * @param string $fifoPath is the location of the fifo file.
     * @return bool|resource
     */
    public static function named(string $fifoPath)  // Arbitrary)
    {

        if (file_exists($fifoPath)) {
            unlink($fifoPath);          // We are always the master, hopefully we'll catch the kill this time
        }

        umask(0000);

        if (!posix_mkfifo($fifoPath, 0666)) {
            print 'Failed to create named Pipe' . PHP_EOL;
            return false;
        }                   # create a named pipe 0644

        #$user = get_current_user();                            // get current process user

        #exec("chown -R {$user} $fifoPath");                    // We need to modify the permissions so users can write to it

        $fifoFile = fopen($fifoPath, 'rb+');              // Now we open the named pipe we Already created

        stream_set_blocking($fifoFile, false);           // setting to true (resource heavy) activates the handshake feature, aka timeout

        return $fifoFile;                                       // File descriptor
    }

    /** Attempt to send a string to a named pipe. This is normally done
     * after forking so error are ungraciously ignored. If you require
     * further error checking you will need to Try {} Catch this..
     * @param string $value
     * @param string $fifoPath
     * @return bool
     */
    public static function send(string $value, string $fifoPath): bool
    {

        try {
            if (!file_exists($fifoPath)) {
                return false; //sortDump('Fuck me!'); // if it does
            }

            umask(0000);

            posix_mkfifo($fifoPath, 0666);              // TODO - don't be a dumbass, start sockets in config

            # $user = get_current_user();                    // get current process user

            // exec("chown -R {$user}:{$user} $fifoPath");    // We need to modify the permissions so users can write to it

            #sortDump(substr(sprintf('%o', fileperms($fifoPath)), -4) . PHP_EOL);  //-- file permissions

            $value .= PHP_EOL; // safely send, dada needs to end in null

            $fifo = fopen($fifoPath, 'wb+');

            fwrite($fifo, $value, \strlen($value) + 1);

            fclose($fifo);
        } catch (\Exception $e) {
            ErrorCatcher::generateLog($e);
        }
        return true;
    }
}


<?php
/**
 * Ongr_Sniffs_WhiteSpace_PHPOpenTagSniff
 *
 * PHP version 5
 *
 * @category PHP
 * @package  Ongr_Strict_Codin_Standard
 * @author   Ongr Team <info@nfq.com>
 * @license  http://spdx.org/licenses/MIT MIT License
 * @link     https://github.com/ongr-io/Ongr
 */

/**
 * Ongr_Sniffs_WhiteSpace_PHPOpenTagSniff
 *
 * Ensures a blank line after php open tag.
 *
 * PHP version 5
 *
 * @category PHP
 * @package  Ongr_Strict_Codin_Standard
 * @author   Ongr Team <info@nfq.com>
 * @license  http://spdx.org/licenses/MIT MIT License
 * @version  Release: @package_version@
 * @link     https://github.com/ongr-io/Ongr
 */
class Ongr_Sniffs_WhiteSpace_PHPOpenTagSniff implements PHP_CodeSniffer_Sniff
{
    /**
     * Returns an array of tokens this test wants to listen for.
     *
     * @return array
     */
    public function register()
    {
        return [T_OPEN_TAG];
    }

    /**
     * Processes this test, when one of its tokens is encountered.
     *
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
     * @param int                  $stackPtr  The position of the current token in
     *                                        the stack passed in $tokens.
     *
     * @return void
     */
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        #TODO add an autofix.
        $tokens = $phpcsFile->getTokens();
        if (isset($tokens[$stackPtr + 1]) && $tokens[$stackPtr + 1]['code'] !== T_WHITESPACE) {
            $error = 'There must be one blank line after the php open tag and no whitespaces';
            $phpcsFile->addError($error, $stackPtr, 'BlankLineAfter');
        }
        $content = $phpcsFile->findNext(T_OPEN_TAG, $stackPtr) + 1;
        while ($tokens[$content]['code'] == T_WHITESPACE) {
            ++$content;
        }
        $spaces = ($tokens[$content]['line'] - $tokens[$stackPtr]['line']) - 1;
        if ($spaces > 1) {
            $error = 'There must be only 1 blank line after the php open tag; Found ' . $spaces;
            $phpcsFile->addError($error, $stackPtr, 'BlankLinesAfter');
        }
    }
}


<?php

namespace beacon;

/**
 * Created by PhpStorm.
 * User: wj008
 * Date: 2017/12/11
 * Time: 2:02
 */
class Utils
{
    /**
     * 路径
     * @param mixed ...$paths
     * @return string
     */
    public static function path(...$paths)
    {
        $protocol = '';
        $path = trim(implode(DIRECTORY_SEPARATOR, $paths));
        if (preg_match('@^([a-z0-9]+://|/)(.*)@i', $path, $m)) {
            $protocol = $m[1];
            $path = $m[2];
        }
        $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
        $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
        $absolutes = [];
        foreach ($parts as $part) {
            if ('.' == $part) {
                continue;
            }
            if ('..' == $part) {
                array_pop($absolutes);
            } else {
                $absolutes[] = $part;
            }
        }
        $path = implode(DIRECTORY_SEPARATOR, $absolutes);
        if (DIRECTORY_SEPARATOR == '\\' && isset($protocol[4])) {
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }
        return $protocol . $path;
    }

    /**
     * 修剪掉root路径
     * @param $path
     * @param string $base
     * @return bool|string
     */
    public static function trimPath($path, $base = ROOT_DIR)
    {
        $len = strlen($base);
        if (strlen($path) > $len) {
            if (substr($path, 0, $len) == $base) {
                $path = substr($path, $len);
            }
        }
        return $path;
    }

    /**
     * 驼峰转下划线
     * @param $name
     * @return null|string|string[]
     */
    public static function toUnder($name)
    {
        $name = preg_replace_callback('@[A-Z]@', function ($m) {
            return '_' . strtolower($m[0]);
        }, $name);
        $name = ltrim($name, '_');
        return $name;
    }

    /**
     * 下划线转驼峰
     * @param $name
     * @return null|string|string[]
     */
    public static function toCamel($name)
    {
        $name = preg_replace('@[_-]+@', '_', $name);
        $name = preg_replace_callback('@_[a-z]@', function ($m) {
            return substr(strtoupper($m[0]), 1);
        }, $name);
        $name = ucfirst($name);
        return $name;
    }

    /**
     * 属性 转 驼峰
     * @param $name
     * @return null|string|string[]
     */
    public static function attrToCamel($name)
    {
        $name = preg_replace_callback('@-[a-z]@', function ($m) {
            return substr(strtoupper($m[0]), 1);
        }, trim($name, '-'));
        $name = lcfirst($name);
        return $name;
    }

    /**
     * 驼峰转属性
     * @param $name
     * @return null|string|string[]
     */
    public static function camelToAttr($name)
    {
        $name = preg_replace_callback('@[A-Z]@', function ($m) {
            return '-' . strtolower($m[0]);
        }, $name);
        $name = ltrim($name, '-');
        return $name;
    }

    /**
     * 随机字母数字
     * @param int $len
     * @return string
     */
    public static function randWord($len = 4)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $word = '';
        for ($i = 0; $i < $len; $i++) {
            $word .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $word;
    }

    /**
     * 随机数字
     * @param int $len
     * @return string
     */
    public static function randNum($len = 4)
    {
        $chars = '0123456789';
        $word = '';
        for ($i = 0; $i < $len; $i++) {
            $word .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $word;
    }

    /**
     * 是否json格式
     * @param $str
     * @return bool
     */
    public static function isJson($str)
    {
        return is_string($str) && !empty($str) && preg_match('@^[\[\{].*[\]\}]$@', $str);
    }

    /**
     * 创建文件夹
     * @param $dir
     * @param int $mode
     */
    public static function makeDir($dir, $mode = 0777)
    {
        if (!is_dir($dir)) {
            $pDir = dirname($dir);
            self::makeDir($pDir);
            @mkdir($dir, $mode);
        }
    }

    /**
     * 扩展数组
     * @param array $a1
     * @param array $a2
     * @return array
     */
    public static function extend(array $a1, array $a2)
    {
        foreach ($a2 as $key => $item) {
            if (!isset($a1[$key])) {
                $a1[$key] = $item;
            } else {
                if (is_array($item) && is_array($a1[$key])) {
                    $a1[$key] = self::extend($a1[$key], $item);
                } else {
                    $a1[$key] = $item;
                }
            }
        }
        return $a1;
    }

}


<?php

/*
 * This file is part of the Plum package.
 *
 * (c) 2010-2012 Julien Brochet <mewt@madalynn.eu>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Plum;

use Plum\Deployer\DeployerInterface;
use Plum\Server\ServerInterface;

class Plum
{
    /**
     * @var array
     */
    protected $servers;

    /**
     * @var array
     */
    protected $deployers;

    /**
     * List of options
     * @var array
     */
    protected $options;

    public function __construct()
    {
        $this->servers   = array();
        $this->deployers = array();
        $this->options   = array();
    }

    /**
     * Registers a deployer
     *
     * @param DeployerInterface $deployer
     *
     * @return \Plum\Plum
     */
    public function registerDeployer(DeployerInterface $deployer)
    {
        if (null !== $deployer) {
            $this->deployers[$deployer->getName()] = $deployer;
        }

        return $this;
    }

    /**
     * Returns deployers
     *
     * @return array
     */
    public function getDeployers()
    {
        return $this->deployers;
    }

    /**
     * Returns a deployer
     *
     * @param string $deployer The deployer name
     *
     * @return \Plum\Deployer\DeployerInterface
     */
    public function getDeployer($deployer)
    {
        if (false === isset($this->deployers[$deployer])) {
            throw new \InvalidArgumentException(sprintf('The deployer "%s" is not registered.', $deployer));
        }

        return $this->deployers[$deployer];
    }

    /**
     * Adds a server to the list
     *
     * @param string $name
     * @param \Plum\Server\ServerInterface $server
     *
     * @return \Plum\Plum
     */
    public function addServer($name, ServerInterface $server)
    {
        if (null === $server) {
            throw new \InvalidArgumentException('The server can not be null.');
        }

        if (true === isset($this->servers[$name])) {
            throw new \InvalidArgumentException(sprintf('The server "%s" is already registered.', $name));
        }

        $this->servers[$name] = $server;

        return $this;
    }

    /**
     * Adds a list of servers
     *
     * @param array $servers
     *
     * @return \Plum\Plum
     */
    public function setServers(array $servers)
    {
        foreach ($servers as $name => $server) {
            $this->addServer($name, $server);
        }

        return $this;
    }

    /**
     * Remove a server
     *
     * @param string $name
     *
     * @return \Plum\Plum
     */
    public function removeServer($name)
    {
        unset($this->servers[$name]);

        return $this;
    }

    /**
     * Returns a server
     *
     * @param type $server The server name
     *
     * @return \Plum\Server\ServerInterface
     */
    public function getServer($server)
    {
        if (!isset($this->servers[$server])) {
            throw new \InvalidArgumentException(sprintf('The server "%s" is not registered.', $server));
        }

        return $this->servers[$server];
    }

    /**
     * Returns servers
     *
     * @return array
     */
    public function getServers()
    {
        return $this->servers;
    }

    /**
     * Deploys to the server using the deployer
     *
     * @param string $server   The name of the server
     * @param string $deployer The name of the deployer
     */
    public function deploy($server, $deployer)
    {
        $server   = $this->getServer($server);
        $deployer = $this->getDeployer($deployer);

        return $deployer->deploy($server, $this->options);
    }

    public function setOptions(array $options)
    {
        $this->options = $options;
    }

    /**
     * Adds a global option
     *
     * @param string $key
     * @param string $value
     */
    public function addOption($key, $value)
    {
        $this->options[$key] = $value;
    }

    /**
     * Returns the array of options
     *
     * @param string $server The server name
     */
    public function getOptions($server = null)
    {
        if (null === $server) {
            return $this->options;
        }

        $server = $this->getServer($server);

        return array_merge($this->options, $server->getOptions());
    }
}


<?php
/**
 * Ongr_Sniffs_Commenting_EmptyCatchCommentSniff.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Marc McIntyre <mmcintyre@squiz.net>
 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

/**
 * Ongr_Sniffs_Commenting_DocCommentAlignmentSniff.
 *
 * Tests that the stars in a doc comment align correctly.
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Marc McIntyre <mmcintyre@squiz.net>
 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
 * @version   Release: @package_version@
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */
class Ongr_Sniffs_Commenting_DocCommentAlignmentSniff implements PHP_CodeSniffer_Sniff
{

    /**
     * A list of tokenizers this sniff supports.
     *
     * @var array
     */
    public $supportedTokenizers = array(
                                   'PHP',
                                   'JS',
                                  );

    /**
     * Returns an array of tokens this test wants to listen for.
     *
     * @return array
     */
    public function register()
    {
        return array(T_DOC_COMMENT_OPEN_TAG);

    }//end register()

    /**
     * Processes this test, when one of its tokens is encountered.
     *
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
     * @param int                  $stackPtr  The position of the current token
     *                                         in the stack passed in $tokens.
     *
     * @return void
     */
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();

        // We are only interested in function/class/interface doc block comments.
        $ignore = PHP_CodeSniffer_Tokens::$emptyTokens;
        if ($phpcsFile->tokenizerType === 'JS') {
            $ignore[] = T_EQUAL;
            $ignore[] = T_STRING;
            $ignore[] = T_OBJECT_OPERATOR;
        }

        $nextToken = $phpcsFile->findNext($ignore, ($stackPtr + 1), null, true);
        $ignore    = array(
                      T_CLASS     => true,
                      T_INTERFACE => true,
                      T_FUNCTION  => true,
                      T_PUBLIC    => true,
                      T_PRIVATE   => true,
                      T_PROTECTED => true,
                      T_STATIC    => true,
                      T_ABSTRACT  => true,
                      T_PROPERTY  => true,
                      T_OBJECT    => true,
                      T_PROTOTYPE => true,
                     );

        if (isset($ignore[$tokens[$nextToken]['code']]) === false) {
            // Could be a file comment.
            $prevToken = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true);
            if ($tokens[$prevToken]['code'] !== T_OPEN_TAG) {
                return;
            }
        }

        // There must be one space after each star (unless it is an empty comment line)
        // and all the stars must be aligned correctly.
        $requiredColumn = ($tokens[$stackPtr]['column'] + 1);
        $endComment     = $tokens[$stackPtr]['comment_closer'];
        for ($i = ($stackPtr + 1); $i <= $endComment; $i++) {
            if ($tokens[$i]['code'] !== T_DOC_COMMENT_STAR
                && $tokens[$i]['code'] !== T_DOC_COMMENT_CLOSE_TAG
            ) {
                continue;
            }

            if ($tokens[$i]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
                // Can't process the close tag if it is not the first thing on the line.
                $prev = $phpcsFile->findPrevious(T_DOC_COMMENT_WHITESPACE, ($i - 1), $stackPtr, true);
                if ($tokens[$prev]['line'] === $tokens[$i]['line']) {
                    continue;
                }
            }

            if ($tokens[$i]['column'] !== $requiredColumn) {
                $error = 'Expected %s space(s) before asterisk; %s found';
                $data  = array(
                          ($requiredColumn - 1),
                          ($tokens[$i]['column'] - 1),
                         );
                $fix   = $phpcsFile->addFixableError($error, $i, 'SpaceBeforeStar', $data);
                if ($fix === true) {
                    $padding = str_repeat(' ', ($requiredColumn - 1));
                    if ($tokens[$i]['column'] === 1) {
                        $phpcsFile->fixer->addContentBefore($i, $padding);
                    } else {
                        $phpcsFile->fixer->replaceToken(($i - 1), $padding);
                    }
                }
            }

            if ($tokens[$i]['code'] !== T_DOC_COMMENT_STAR) {
                continue;
            }

            if ($tokens[($i + 2)]['line'] !== $tokens[$i]['line']) {
                // Line is empty.
                continue;
            }
            //ONGR get last character on previous line.
            $lastVarEnding = substr($tokens[$i - 3]['content'], -1);
            if ($tokens[($i + 1)]['code'] !== T_DOC_COMMENT_WHITESPACE) {
                $error = 'Expected 1 space after asterisk; 0 found';
                $fix   = $phpcsFile->addFixableError($error, $i, 'NoSpaceAfterStar');
                if ($fix === true) {
                    $phpcsFile->fixer->addContent($i, ' ');
                }
            } else if ($tokens[($i + 2)]['code'] === T_DOC_COMMENT_TAG
                && $tokens[($i + 1)]['content'] !== ' '
                //ONGR we allow more spaces after asterisk if tags represented as in array.
                && $lastVarEnding !== '{'
                && $lastVarEnding !== ','
            ) {
                $error = 'Expected 1 space after asterisk; %s found   1 '.$lastVarEnding;
                $data  = array(strlen($tokens[($i + 1)]['content']));
                $fix   = $phpcsFile->addFixableError($error, $i, 'SpaceAfterStar', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->replaceToken(($i + 1), ' ');
                }
            }
        }//end for

    }//end process()

}//end class