<?php
/**
 * Sniffs_Squiz_WhiteSpace_OperatorSpacingSniff.
 *
 * 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
 */

/**
 * Sniffs_Squiz_WhiteSpace_OperatorSpacingSniff.
 *
 * Verifies that operators have valid spacing surrounding them.
 *
 * @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_WhiteSpace_OperatorSpacingSniff implements PHP_CodeSniffer_Sniff
{

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

    /**
     * Allow newlines instead of spaces.
     *
     * @var boolean
     */
    public $ignoreNewlines = false;

    /**
     * Returns an array of tokens this test wants to listen for.
     *
     * @return array
     */
    public function register()
    {
        $comparison = PHP_CodeSniffer_Tokens::$comparisonTokens;
        $operators  = PHP_CodeSniffer_Tokens::$operators;
        $assignment = PHP_CodeSniffer_Tokens::$assignmentTokens;
        $inlineIf   = array(
                       T_INLINE_THEN,
                       T_INLINE_ELSE,
                      );

        return array_unique(
            array_merge($comparison, $operators, $assignment, $inlineIf, [T_BOOLEAN_NOT])
        );

    }//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 check space after not operator.
        if ($tokens[$stackPtr]['code'] === T_BOOLEAN_NOT && $tokens[$stackPtr + 1]['code'] === T_WHITESPACE) {
            $error = 'Whitespace found after not operator';
            $phpcsFile->addError($error, $stackPtr, 'SpacingAfterNot');
            return;
        }

        //Ongr. If it's correct usage of bool operator skip it.
        if ($tokens[$stackPtr]['code'] === T_BOOLEAN_NOT) {
            // Skip for '!' case.
            return;
        }

        // Skip default values in function declarations.
        if ($tokens[$stackPtr]['code'] === T_EQUAL
            || $tokens[$stackPtr]['code'] === T_MINUS
        ) {
            if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
                $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']);
                $bracket     = array_pop($parenthesis);
                if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
                    $function = $tokens[$bracket]['parenthesis_owner'];
                    if ($tokens[$function]['code'] === T_FUNCTION
                        || $tokens[$function]['code'] === T_CLOSURE
                    ) {
                        return;
                    }
                }
            }
        }

        if ($tokens[$stackPtr]['code'] === T_EQUAL) {
            // Skip for '=&' case.
            if (isset($tokens[($stackPtr + 1)]) === true
                && $tokens[($stackPtr + 1)]['code'] === T_BITWISE_AND
            ) {
                return;
            }
        }

        // Skip short ternary such as: "$foo = $bar ?: true;".
        if (($tokens[$stackPtr]['code'] === T_INLINE_THEN
            && $tokens[($stackPtr + 1)]['code'] === T_INLINE_ELSE)
            || ($tokens[($stackPtr - 1)]['code'] === T_INLINE_THEN
            && $tokens[$stackPtr]['code'] === T_INLINE_ELSE)
        ) {
                return;
        }

        if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) {
            // If it's not a reference, then we expect one space either side of the
            // bitwise operator.
            if ($phpcsFile->isReference($stackPtr) === true) {
                return;
            }

            // Check there is one space before the & operator.
            if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
                $error = 'Expected 1 space before "&" operator; 0 found';
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAmp');
                if ($fix === true) {
                    $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
                }

                $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
            } else {
                if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
                    $found = 'newline';
                } else {
                    $found = $tokens[($stackPtr - 1)]['length'];
                }

                $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
                if ($found !== 1
                    && ($found !== 'newline' || $this->ignoreNewlines === false)
                ) {
                    $error = 'Expected 1 space before "&" operator; %s found';
                    $data  = array($found);
                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeAmp', $data);
                    if ($fix === true) {
                        $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
                    }
                }
            }//end if

            // Check there is one space after the & operator.
            if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
                $error = 'Expected 1 space after "&" operator; 0 found';
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAmp');
                if ($fix === true) {
                    $phpcsFile->fixer->addContent($stackPtr, ' ');
                }

                $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
            } else {
                if ($tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']) {
                    $found = 'newline';
                } else {
                    $found = $tokens[($stackPtr + 1)]['length'];
                }

                $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
                if ($found !== 1
                    && ($found !== 'newline' || $this->ignoreNewlines === false)
                ) {
                    $error = 'Expected 1 space after "&" operator; %s found';
                    $data  = array($found);
                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterAmp', $data);
                    if ($fix === true) {
                        $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
                    }
                }
            }//end if

            return;
        }//end if

        if ($tokens[$stackPtr]['code'] === T_MINUS) {
            // Check minus spacing, but make sure we aren't just assigning
            // a minus value or returning one.
            $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
            if ($tokens[$prev]['code'] === T_RETURN) {
                // Just returning a negative value; eg. (return -1).
                return;
            }

            if (isset(PHP_CodeSniffer_Tokens::$operators[$tokens[$prev]['code']]) === true) {
                // Just trying to operate on a negative value; eg. ($var * -1).
                return;
            }

            if (isset(PHP_CodeSniffer_Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) {
                // Just trying to compare a negative value; eg. ($var === -1).
                return;
            }

            if (isset(PHP_CodeSniffer_Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
                // Just trying to compare a negative value; eg. ($var || -1 === $b).
                return;
            }

            if (isset(PHP_CodeSniffer_Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) {
                // Just trying to assign a negative value; eg. ($var = -1).
                return;
            }

            // A list of tokens that indicate that the token is not
            // part of an arithmetic operation.
            $invalidTokens = array(
                              T_COMMA               => true,
                              T_OPEN_PARENTHESIS    => true,
                              T_OPEN_SQUARE_BRACKET => true,
                              T_DOUBLE_ARROW        => true,
                              T_COLON               => true,
                              T_INLINE_THEN         => true,
                              T_INLINE_ELSE         => true,
                              T_CASE                => true,
                             );

            if (isset($invalidTokens[$tokens[$prev]['code']]) === true) {
                // Just trying to use a negative value; eg. myFunction($var, -2).
                return;
            }
        }//end if

        $operator = $tokens[$stackPtr]['content'];

        if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
            $error = "Expected 1 space before \"$operator\"; 0 found";
            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore');
            if ($fix === true) {
                $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
            }

            $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
        } else if (isset(PHP_CodeSniffer_Tokens::$assignmentTokens[$tokens[$stackPtr]['code']]) === false) {
            // Don't throw an error for assignments, because other standards allow
            // multiple spaces there to align multiple assignments.
            if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
                $found = 'newline';
            } else {
                $found = $tokens[($stackPtr - 1)]['length'];
            }

            $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
            if ($found !== 1
                && ($found !== 'newline' || $this->ignoreNewlines === false)
            ) {
                $error = 'Expected 1 space before "%s"; %s found';
                $data  = array(
                          $operator,
                          $found,
                         );
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->beginChangeset();
                    if ($found === 'newline') {
                        $i = ($stackPtr - 2);
                        while ($tokens[$i]['code'] === T_WHITESPACE) {
                            $phpcsFile->fixer->replaceToken($i, '');
                            $i--;
                        }
                    }

                    $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
                    $phpcsFile->fixer->endChangeset();
                }
            }//end if
        }//end if

        if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
            $error = "Expected 1 space after \"$operator\"; 0 found";
            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter');
            if ($fix === true) {
                $phpcsFile->fixer->addContent($stackPtr, ' ');
            }

            $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
        } else {
            if ($tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']) {
                $found = 'newline';
            } else {
                $found = $tokens[($stackPtr + 1)]['length'];
            }

            $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
            if ($found !== 1
                && ($found !== 'newline' || $this->ignoreNewlines === false)
            ) {
                $error = 'Expected 1 space after "%s"; %s found';
                $data  = array(
                          $operator,
                          $found,
                         );
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfter', $data);
                if ($fix === true) {
                    $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
                }
            }
        }//end if

    }//end process()

}//end class


<?php

/**
 * This file is part of the Symfony2-coding-standard (phpcs standard)
 *
 * PHP version 5
 *
 * @category PHP
 * @package  PHP_CodeSniffer-Symfony2
 * @author   Symfony2-phpcs-authors <Symfony2-coding-standard@escapestudios.github.com>
 * @license  http://spdx.org/licenses/MIT MIT License
 * @version  GIT: master
 * @link     https://github.com/escapestudios/Symfony2-coding-standard
 */

/**
 * Symfony2_Sniffs_Formatting_BlankLineBeforeReturnSniff.
 *
 * Throws errors if there's no blank line before return statements. Symfony
 * coding standard specifies: "Add a blank line before return statements,
 * unless the return is alone inside a statement-group (like an if statement);"
 *
 * @category PHP
 * @package  PHP_CodeSniffer-Symfony2
 * @author   Dave Hauenstein <davehauenstein@gmail.com>
 * @license  http://spdx.org/licenses/MIT MIT License
 * @link     https://github.com/escapestudios/Symfony2-coding-standard
 */
class Ongr_Sniffs_Symfony_BlankLineBeforeReturnSniff 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_RETURN);
    }

    /**
     * Processes this test, when one of its tokens is encountered.
     *
     * @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
     * @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();
        $current         = $stackPtr;
        $previousLine    = $tokens[$stackPtr]['line'] - 1;
        $prevLineTokens  = array();

        while ($current >= 0 && $tokens[$current]['line'] >= $previousLine) {
            if ($tokens[$current]['line'] == $previousLine
                && $tokens[$current]['type'] !== 'T_WHITESPACE'
                && $tokens[$current]['type'] !== 'T_COMMENT'
            ) {
                $prevLineTokens[] = $tokens[$current]['type'];
            }
            $current--;
        }

        if (isset($prevLineTokens[0])
            && ($prevLineTokens[0] === 'T_OPEN_CURLY_BRACKET'
            || $prevLineTokens[0] === 'T_COLON')
        ) {
            return;
        } else if (count($prevLineTokens) > 0) {
            $phpcsFile->addError(
                'Missing blank line before return statement',
                $stackPtr
            );
        }

        return;
    }
}


<?php
/* Create a new class called Bcrypt */
/*
 *
*/

namespace CarbonPHP\helpers;

class Bcrypt
{
    private static $rounds = 10;

    // http://php.net/manual/en/language.operators.bitwise.php
    // in actual system we will have to see what bit system we are using
    // 32 bit = 28
    // 64 biit = 60
    // I assume this means php uses 4 bits to denote type (vzal) ?
    // idk
    // godaddy has me on a 64 bit computer
    public static function genRandomHex($bitLength = 40)
    {
        $sudoRandom = 1;
        for ($i=0;$i<=$bitLength;$i++) $sudoRandom = ($sudoRandom<<1)|rand(0,1);
        return dechex($sudoRandom);
    }

    private static function genSalt()
    {
        /* GenSalt */
        $string = str_shuffle( mt_rand() );
        return uniqid( $string, true );
    }

    /* Gen Hash */
    public static function genHash($password)
    {
        if (CRYPT_BLOWFISH != 1)
            throw new \Exception( "Bcrypt is not supported on this server, please see the following to learn more: http://php.net/crypt" );

        /* Explain '$2y$' . $this->rounds . '$' */
        /* 2y selects bcrypt algorithm */
        /* $this->rounds is the workload factor */
        /* GenHash */
        $hash = crypt( $password, '$2y$' . self::$rounds . '$' . self::genSalt() );
        /* Return */
        return $hash;
    }

    /* Verify Password */
    public static function verify($password, $existingHash)
    {
        /* Hash new password with old hash */

        $hash = crypt( $password, $existingHash );

        /* Do Hashs match? */
        if ($hash === $existingHash) {
            return true;
        } else {
            return false;
        }
    }
}


<?php

namespace beacon;

/**
 * Created by PhpStorm.
 * User: wj008
 * Date: 2017/12/13
 * Time: 16:51
 */
class DB
{
    private static $engine = null;

    /**
     * 获取数据库引擎实例
     * @return Mysql|null
     * @throws MysqlException
     */
    public static function engine()
    {
        if (self::$engine != null) {
            return self::$engine;
        }
        $driver = Config::get('db.db_driver', 'Mysql');
        if ($driver == 'Mysql') {
            self::$engine = Mysql::instance();
            return self::$engine;
        }
        throw new \Exception('不支持的数据库驱动类型');
    }

    /**
     * 开启事务
     * @return bool
     * @throws MysqlException
     */
    public static function beginTransaction()
    {
        return self::engine()->beginTransaction();
    }

    /**
     * 是否在事物里面
     * @return bool
     * @throws MysqlException
     */
    public static function inTransaction()
    {
        return self::engine()->inTransaction();
    }

    /**
     * 事务闭包
     * @param $func
     * @throws \Exception
     */
    public static function transaction($func)
    {
        try {
            DB::beginTransaction();
            $func();
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            throw $exception;
        }
    }

    /**
     * 提交事务
     * @return bool
     * @throws MysqlException
     */
    public static function commit()
    {
        return self::engine()->commit();
    }

    /**
     * 回滚事务
     * @return bool
     * @throws MysqlException
     */
    public static function rollBack()
    {
        return self::engine()->rollBack();
    }

    /**
     * 执行sql 语句
     * @param string $sql
     * @return int
     * @throws MysqlException
     */
    public static function exec(string $sql)
    {
        return self::engine()->exec($sql);
    }

    /**
     * 获取最后一条sql 语句,需要开启 DEBUG_MYSQL_LOG
     * @return string
     * @throws MysqlException
     */
    public static function lastSql()
    {
        return self::engine()->lastSql();
    }

    /**
     * 获取最后的插入的id
     * @param null $name
     * @return string
     * @throws MysqlException
     */
    public static function lastInsertId($name = null)
    {
        return self::engine()->lastInsertId($name);
    }

    /**
     * 执行sql 语句
     * @param string $sql
     * @param null $args
     * @return bool|\PDOStatement
     * @throws MysqlException
     */
    public static function execute(string $sql, $args = null)
    {
        return self::engine()->execute($sql, $args);
    }

    /**
     * 获取多行记录
     * @param string $sql
     * @param null $args
     * @param null $fetch_style
     * @param null $fetch_argument
     * @param array|null $ctor_args
     * @return array
     * @throws MysqlException
     */
    public static function getList(string $sql, $args = null, $fetch_style = null, $fetch_argument = null, array $ctor_args = null)
    {
        return self::engine()->getList($sql, $args, $fetch_style, $fetch_argument, $ctor_args);
    }

    /**
     * 获取单条数据
     * @param string $sql
     * @param null $args
     * @param null $fetch_style
     * @param null $cursor_orientation
     * @param int $cursor_offset
     * @return mixed|null
     * @throws MysqlException
     */
    public static function getRow(string $sql, $args = null, $fetch_style = null, $cursor_orientation = null, $cursor_offset = 0)
    {
        return self::engine()->getRow($sql, $args, $fetch_style, $cursor_orientation, $cursor_offset);
    }

    /**
     * 获取单个字段值
     * @param string $sql
     * @param null $args
     * @param null $field
     * @return mixed|null
     * @throws MysqlException
     */
    public static function getOne(string $sql, $args = null, $field = null)
    {
        return self::engine()->getOne($sql, $args, $field);
    }

    /**
     * 获取某个字段的最大值
     * @param string $tbname
     * @param string $field
     * @param null $where
     * @param null $args
     * @return null
     * @throws MysqlException
     */
    public static function getMax(string $tbname, string $field, $where = null, $args = null)
    {
        return self::engine()->getMax($tbname, $field, $where, $args);
    }

    /**
     * 获取某个字段的最小值
     * @param string $tbname
     * @param string $field
     * @param null $where
     * @param null $args
     * @return null
     * @throws MysqlException
     */
    public static function getMin(string $tbname, string $field, $where = null, $args = null)
    {
        return self::engine()->getMin($tbname, $field, $where, $args);
    }

    /**
     *  创建一个sql语句片段,一般用于更新 插入数据时数组的值
     * @param string $sql
     * @param null $args
     * @return SqlRaw
     * @throws MysqlException
     */
    public static function raw(string $sql, $args = null)
    {
        return self::engine()->raw($sql, $args);
    }

    /**
     * 插入记录
     * @param string $tbname
     * @param array $values
     * @throws MysqlException
     */
    public static function insert(string $tbname, array $values = [])
    {
        return self::engine()->insert($tbname, $values);
    }

    /**
     * 替换记录集
     * @param string $tbname
     * @param array $values
     * @throws MysqlException
     */
    public static function replace(string $tbname, array $values = [])
    {
        return self::engine()->replace($tbname, $values);
    }

    /**
     * 更新记录集
     * @param string $tbname
     * @param array $values
     * @param null $where
     * @param null $args
     * @throws MysqlException
     */
    public static function update(string $tbname, array $values, $where = null, $args = null)
    {
        return self::engine()->update($tbname, $values, $where, $args);
    }

    /**
     * 删除记录集
     * @param string $tbname
     * @param null $where
     * @param null $args
     * @throws MysqlException
     */
    public static function delete(string $tbname, $where = null, $args = null)
    {
        return self::engine()->delete($tbname, $where, $args);
    }

    /**
     * 获取表字段 判断字段是否存在
     * @param string $tbname
     * @return array
     * @throws MysqlException
     */
    public static function getFields(string $tbname)
    {
        return self::engine()->getFields($tbname);
    }

    /**
     * 对数据表进行过滤字段
     * @param array $data
     * @param string $tbname
     * @return array
     * @throws MysqlException
     */
    public static function fieldFilter(array $data, string $tbname)
    {
        $fields = self::engine()->getFields($tbname);
        $temp = [];
        foreach ($fields as $field) {
            $key = $field['Field'];
            if (isset($data[$key])) {
                $temp[$key] = $data[$key];
            }
        }
        return $temp;
    }

    /**
     * 判断字段是否存在
     * @param string $tbname
     * @param string $field
     * @return bool
     * @throws MysqlException
     */
    public static function existsField(string $tbname, string $field)
    {
        return self::engine()->existsField($tbname, $field);
    }

    /**
     * 创建数据库表
     * @param string $tbname
     * @param array $options
     * @throws MysqlException
     */
    public static function createTable(string $tbname, array $options = [])
    {
        return self::engine()->createTable($tbname, $options);
    }

    /**
     * 添加字段
     * @param string $tbname
     * @param string $field
     * @param array $options
     * @return int
     * @throws MysqlException
     */
    public static function addField(string $tbname, string $field, array $options = [])
    {
        return self::engine()->addField($tbname, $field, $options);
    }

    /**
     * 修改字段
     * @param string $tbname
     * @param string $field
     * @param array $options
     * @return int
     * @throws MysqlException
     */
    public static function modifyField(string $tbname, string $field, array $options = [])
    {
        return self::engine()->modifyField($tbname, $field, $options);
    }

    /**
     * 更新字段
     * @param string $tbname
     * @param string $oldfield
     * @param string $newfield
     * @param array $options
     * @return int
     * @throws MysqlException
     */
    public static function updateField(string $tbname, string $oldfield, string $newfield, array $options = [])
    {
        return self::engine()->updateField($tbname, $oldfield, $newfield, $options);
    }

    /**
     * 删除字段
     * @param string $tbname
     * @param string $field
     * @return int|null
     * @throws MysqlException
     */
    public static function dropField(string $tbname, string $field)
    {
        return self::engine()->dropField($tbname, $field);
    }

    /**
     * 检查表是否存在
     * @param string $tbname
     * @return bool
     * @throws MysqlException
     */
    public static function existsTable(string $tbname)
    {
        return self::engine()->existsTable($tbname);
    }

    /**
     * 删除表
     * @param string $tbname
     * @return int
     * @throws MysqlException
     */
    public static function dropTable(string $tbname)
    {
        return self::engine()->dropTable($tbname);
    }
}


<?php

/**
 * This has been edited to include an auto serialized method.
 * Variables given to the start function will be cached between requests.
 *
 * This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details.
 */

namespace CarbonPHP\Helpers;

abstract class Serialized {

    /**
     * @var array $sessionVar is an array who's values equal variables
     * names in the global scope.
     */
    private static $sessionVar;

    /** Variables given will be cached between requests.
     * Variables should be provided as string names referencing
     * the global scope.
     * @param array ...$argv
     */
    public static function start(...$argv) : void
	{
	    //TODO - make base64 optional
        self::$sessionVar = $argv;
		foreach (self::$sessionVar as $value){
			if (isset($_SESSION[__CLASS__][$value])) {
                $GLOBALS[$value] = $_SESSION[__CLASS__][$value];
                #self::is_serialized( base64_decode(  $_SESSION[__CLASS__][$value] ), $GLOBALS[$value] );
            }
		}

		// You CAN register multiple shutdown functions
		register_shutdown_function( function () use ($argv) {
            $last_error = error_get_last();
            if ($last_error['type'] === E_ERROR) {
                sortDump($last_error);
            } else {
                foreach ($argv as $value) {
                    if (isset($GLOBALS[$value])) {
                        $_SESSION[__CLASS__][$value] = $GLOBALS[$value];
                        #$_SESSION[__CLASS__][$value] = base64_encode( serialize( $GLOBALS[$value] ) );
                    }
                }
            }
		} );
	}

    /**
     * Clear any variables given to the start method.
     * This does not stop caching, just removes all data
     * from current variables and cache.
     */
    public static function clear() : void
    {
        if (!empty(self::$sessionVar)) {
            foreach (self::$sessionVar as $value) {
                $GLOBALS[$value] = $_SESSION[$value] = null;
            }
        }
    }

    /**
     * Tests if an input is valid PHP serialized string.
     *
     * Checks if a string is serialized using quick string manipulation
     * to throw out obviously incorrect strings. Unserialize is then run
     * on the string to perform the final verification.
     *
     * Valid serialized forms are the following:
     *

     *
boolean: b:1;

     *
integer: i:1;

     *
double: d:0.2;

     *
string: s:4:"test";

     *
array: a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}

     *
object: O:8:"stdClass":0:{}

     *
null: N;

     *

     *
     * @author		Chris Smith
     * @copyright	Copyright (c) 2009 Chris Smith (http://www.cs278.org/)
     * @license		http://sam.zoy.org/wtfpl/ WTFPL
     * @param		string	$value	Value to test for serialized form
     * @param		mixed	$result	Result of unserialize() of the $value
     * @return		boolean			True if $value is serialized data, otherwise false
     */
	public static function is_serialized($value, &$result = null) : bool
	{
		// Bit of a give away this one
		if (!\is_string( $value )) {
            return false;
        }

		// Serialized false, return true. unserialize() returns false on an
		// invalid string or it could return false if the string is serialized
		// false, eliminate that possibility.
		if ($value === 'b:0;') {
			$result = false;
			return true;
		}

		$length = \strlen( $value );
		$end = '';

		switch ($value[0]) {
			case 's':
				if ($value[$length - 2] !== '"') {
					return false;
				}
			case 'b':
			case 'i':
			case 'd':
				// This looks odd but it is quicker than isset()ing
				$end .= ';';
			case 'a':
			case 'O':
				$end .= '}';

				if ($value[1] !== ':') {
					return false;
				}

				switch ($value[2]) {
					case 0:
					case 1:
					case 2:
					case 3:
					case 4:
					case 5:
					case 6:
					case 7:
					case 8:
					case 9:
						break;

					default:
						return false;
				}
			case 'N':
				$end .= ';';

				if ($value[$length - 1] !== $end[0]) {
					return false;
				}
				break;

			default:
				return false;
		}

		if (($result = @unserialize( $value, true )) === false) {
			$result = null;
			return false;
		}
		return true;
	}
}


<?php
/**
 * Ongr_Sniffs_PHP_DisallowMultipleAssignmentsSniff.
 *
 * 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_PHP_DisallowMultipleAssignmentsSniff.
 *
 * Ensures that there is only one value assignment on a line, and that it is
 * the first thing on the line.
 *
 * @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_PHP_DisallowMultipleAssignmentsSniff implements PHP_CodeSniffer_Sniff
{

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

    }//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();

        // Ignore default value assignments in function definitions.
        $function = $phpcsFile->findPrevious(array(T_FUNCTION, T_CLOSURE), ($stackPtr - 1), null, false, null, true);
        if ($function !== false) {
            $opener = $tokens[$function]['parenthesis_opener'];
            $closer = $tokens[$function]['parenthesis_closer'];
            if ($opener < $stackPtr && $closer > $stackPtr) {
                return;
            }
        }

        /*
            The general rule is:
            Find an equal sign and go backwards along the line. If you hit an
            end bracket, skip to the opening bracket. When you find a variable,
            stop. That variable must be the first non-empty token on the line
            or in the statement. If not, throw an error.
        */

        for ($varToken = ($stackPtr - 1); $varToken >= 0; $varToken--) {
            // Skip brackets.
            if (isset($tokens[$varToken]['parenthesis_opener']) === true && $tokens[$varToken]['parenthesis_opener'] < $varToken) {
                $varToken = $tokens[$varToken]['parenthesis_opener'];
                continue;
            }

            if (isset($tokens[$varToken]['bracket_opener']) === true) {
                $varToken = $tokens[$varToken]['bracket_opener'];
                continue;
            }

            if ($tokens[$varToken]['code'] === T_SEMICOLON) {
                // We've reached the next statement, so we
                // didn't find a variable.
                return;
            }

            if ($tokens[$varToken]['code'] === T_VARIABLE) {
                // We found our variable.
                break;
            }
        }//end for

        if ($varToken <= 0) {
            // Didn't find a variable.
            return;
        }

        // Deal with this type of variable: self::$var by setting the var
        // token to be "self" rather than "$var".
        if ($tokens[($varToken - 1)]['code'] === T_DOUBLE_COLON) {
            $varToken = ($varToken - 2);
        }

        // Deal with this type of variable: $obj->$var by setting the var
        // token to be "$obj" rather than "$var".
        if ($tokens[($varToken - 1)]['code'] === T_OBJECT_OPERATOR) {
            $varToken = ($varToken - 2);
        }

        // Deal with this type of variable: $$var by setting the var
        // token to be "$" rather than "$var".
        if ($tokens[($varToken - 1)]['content'] === '$') {
            $varToken--;
        }

        // Ignore member var definitions.
        $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($varToken - 1), null, true);
        if (isset(PHP_CodeSniffer_Tokens::$scopeModifiers[$tokens[$prev]['code']]) === true) {
            return;
        }

        if ($tokens[$prev]['code'] === T_STATIC) {
            return;
        }

        // Make sure this variable is the first thing in the statement.
        $varLine  = $tokens[$varToken]['line'];
        $prevLine = 0;
        for ($i = ($varToken - 1); $i >= 0; $i--) {
            if ($tokens[$i]['code'] === T_SEMICOLON) {
                // We reached the end of the statement.
                return;
            }

            if ($tokens[$i]['code'] === T_INLINE_THEN) {
                // We reached the end of the inline THEN statement.
                return;
            }

            if ($tokens[$i]['code'] === T_INLINE_ELSE) {
                // We reached the end of the inline ELSE statement.
                return;
            }

            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$i]['code']]) === false) {
                $prevLine = $tokens[$i]['line'];
                break;
            }
        }//end for

        // Ignore the first part of FOR loops as we are allowed to
        // assign variables there even though the variable is not the
        // first thing on the line. Also ignore WHILE loops.
        if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
            if (isset($tokens[$i]['parenthesis_owner']) !== true) {
                $ownerIndex = $i;
                // Find owner - there must be one!
                while (isset($tokens[$ownerIndex]['parenthesis_owner']) === false && $ownerIndex > 0) {
                    $ownerIndex--;
                }
                if ($ownerIndex > 0) {
                    // Owner index found.
                    $i = $ownerIndex;
                }
            }
            if (isset($tokens[$i]['parenthesis_owner'])) {
                $owner = $tokens[$i]['parenthesis_owner'];
                if ($tokens[$owner]['code'] === T_FOR) {
                    return;
                }
            }
        }

        // Also ignore WHILE loops and IF conditions.
        $validTokens = [T_WHILE, T_IF];
        if (isset($tokens[$stackPtr]['nested_parenthesis'])) {
            foreach ($tokens[$stackPtr]['nested_parenthesis'] as $start => $end) {
                if (isset($tokens[$start]['parenthesis_owner'])
                    && in_array($tokens[$tokens[$start]['parenthesis_owner']]['code'], $validTokens, true)
                ) {
                    return;
                }
            }
        }

        if ($prevLine === $varLine) {
            $error = 'Assignments must be the first block of code on a line';
            $phpcsFile->addError($error, $stackPtr, 'Found');
        }

    }//end process()

}//end class


<?php

namespace beacon;

/**
 * Created by PhpStorm.
 * User: wj008
 * Date: 2017/12/13
 * Time: 14:03
 */

use PDO as PDO;

/**
 * sql 语句片段,用于更新插入时使用
 * Class SqlSection
 * @package beacon
 */
class SqlRaw
{
    public $sql = null;
    public $args = null;

    public function __construct(string $sql, $args = null)
    {
        $this->sql = $sql;
        $this->args = $args;
    }

    public function format()
    {
        return Mysql::format($this->sql, $this->args);
    }
}

/**
 * 错误处理
 * Class MysqlException
 * @package beacon
 */
class MysqlException extends \Exception
{

    protected $detail = '';

    public function __construct(string $message = '', $detail = '', int $code = 0, \Throwable $previous = null)
    {
        $this->detail = $detail;
        parent::__construct($message, $code, $previous);
    }

    public function getDetail()
    {
        return $this->detail;
    }
}

/**
 * mysql 数据操作类
 * Class Mysql
 * @package beacon
 */
class Mysql
{
    private static $instance = null;

    /**
     * 获取一个单例
     * @return Mysql|null
     * @throws MysqlException
     */
    public static function instance()
    {
        if (self::$instance == null) {
            $host = Config::get('db.db_host', '127.0.0.1');
            $port = Config::get('db.db_port', 3306);
            $name = Config::get('db.db_name', '');
            $user = Config::get('db.db_user', '');
            $pass = Config::get('db.db_pwd', '');
            $prefix = Config::get('db.db_prefix', 'sl_');
            $charset = Config::get('db.db_charset', 'utf8');
            try {
                self::$instance = new Mysql($host, $port, $name, $user, $pass, $prefix, $charset);
            } catch (\PDOException $e) {
                self::$instance = null;
                throw new MysqlException($e->getMessage(), '', $e->getCode(), $e);
            } catch (\Exception $e) {
                self::$instance = null;
                throw new MysqlException($e->getMessage(), '', $e->getCode(), $e);
            }
        }
        return self::$instance;
    }

    /**
     * 编码 sql 语句
     * @param $value
     * @return false|int|string
     */
    public static function escape($value)
    {
        if ($value === null) {
            return 'NULL';
        }
        $type = gettype($value);
        switch ($type) {
            case 'bool':
            case 'boolean':
                return $value ? 1 : 0;
            case 'int':
            case 'integer':
            case 'double':
            case 'float':
                return $value;
            case 'string':
                break;
            case 'array':
            case 'object':
                $value = json_encode($value, JSON_UNESCAPED_UNICODE);
                break;
            default :
                $value = strval($value);
                break;
        }
        $value = '\'' . preg_replace_callback('@[\0\b\t\n\r\x1a\"\'\\\\]@', function ($m) {
                switch ($m[0]) {
                    case '\0':
                        return '\\0';
                    case '\b':
                        return '\\b';
                    case '\t':
                        return '\\t';
                    case '\n':
                        return '\\n';
                    case '\r':
                        return '\\r';
                    case '\x1a':
                        return '\\Z';
                    case '"':
                        return '\\"';
                    case '\'':
                        return '\\\'';
                    case '\\':
                        return '\\\\';
                    default:
                        return '';
                }
            }, $value) . '\'';
        return $value;
    }

    /**
     * 格式化 sql 语句
     * @param string $sql
     * @param null $args
     * @return null|string|string[]
     */
    public static function format(string $sql, $args = null)
    {
        if ($args == null) {
            return $sql;
        }
        if (!is_array($args)) {
            $args = [$args];
        }
        if (preg_match('@\?@', $sql)) {
            $index = 0;
            $sql = preg_replace_callback('@\?@', function ($match) use (&$args, &$index) {
                if (!isset($args[$index])) {
                    $index++;
                    return '?';
                }
                $value = $args[$index];
                $index++;
                return Mysql::escape($value);
            }, $sql);
        }
        return $sql;
    }

    /**
     * 前缀
     * @var string
     */
    private $prefix = '';
    /**
     * @var \PDO|null
     */
    private $pdo = null;
    /**
     * 事务计数器,防止多次调用
     * @var int
     */
    private $transactionCounter = 0;
    private $_lastSql = '';

    private $link = null;
    private $user = null;
    private $pass = null;
    private $charset = 'utf8';

    /**
     * 构造函数
     * Mysql constructor.
     * @param $host
     * @param int $port
     * @param string $name
     * @param string $user
     * @param string $pass
     * @param string $prefix
     * @param string $charset
     */
    public function __construct($host, $port = 3306, $name = '', $user = '', $pass = '', $prefix = '', $charset = 'utf8')
    {
        $this->prefix = $prefix;
        if (!empty($name)) {
            $link = 'mysql:host=' . $host . ';port=' . $port . ';dbname=' . $name;
        } else {
            $link = 'mysql:host=' . $host . ';port=' . $port . ';';
        }
        $this->link = $link;
        $this->user = $user;
        $this->pass = $pass;
        $this->charset = $charset;
        try {
            $this->pdo = new PDO($link, $user, $pass, [PDO::ATTR_PERSISTENT => true, PDO::ATTR_TIMEOUT => 120, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . $this->charset]);
        } catch (\PDOException $exc) {
            throw $exc;
        }
    }

    public function reconnection()
    {
        try {
            $this->pdo = new PDO($this->link, $this->user, $this->pass, [PDO::ATTR_PERSISTENT => true, PDO::ATTR_TIMEOUT => 120, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES " . $this->charset]);
        } catch (\PDOException $exc) {
            throw $exc;
        }
    }

    /**
     * 开启事务
     * @return bool
     */
    public function beginTransaction()
    {
        if (!$this->transactionCounter++) {
            return $this->pdo->beginTransaction();
        }
        $this->pdo->exec('SAVEPOINT trans' . $this->transactionCounter);
        return $this->transactionCounter >= 0;
    }

    /**
     * 是否在事务里面
     * @return bool
     */
    public function inTransaction()
    {
        return $this->transactionCounter > 0;
    }

    /**
     * 提交事务
     * @return bool
     */
    public function commit()
    {
        if (!--$this->transactionCounter) {
            return $this->pdo->commit();
        }
        return $this->transactionCounter >= 0;
    }

    /**
     * 回滚事务
     * @return bool
     * @throws MysqlException
     */
    public function rollBack()
    {
        if (--$this->transactionCounter) {
            $this->exec('ROLLBACK TO trans' . ($this->transactionCounter + 1));
            return true;
        }
        return $this->pdo->rollBack();
    }

    /**
     * 执行sql
     * @param string $sql
     * @return int
     * @throws MysqlException
     */
    public function exec(string $sql)
    {
        try {
            $sql = str_replace('@pf_', $this->prefix, $sql);
            $ret = $this->pdo->exec($sql);
            return $ret;
        } catch (\Exception $exception) {
            throw new MysqlException($exception->getMessage(), $sql, $exception->getCode(), $exception);
        }
    }

    /**
     * 获取最后的插入的id
     * @param null $name
     * @return string
     */
    public function lastInsertId($name = null)
    {
        return $this->pdo->lastInsertId($name);
    }

    /**
     * 获取最后执行语句,需要开启 DEBUG_MYSQL_LOG
     * @return string
     */
    public function lastSql()
    {
        return $this->_lastSql;
    }

    /**
     * 执行sql 语句
     * @param string $sql
     * @param null $args
     * @return bool|\PDOStatement
     * @throws MysqlException
     */
    public function execute(string $sql, $args = null)
    {
        $sql = str_replace('@pf_', $this->prefix, $sql);
        if ($args !== null && !is_array($args)) {
            $args = [$args];
        }
        $time = 0;
        if (defined('DEBUG_MYSQL_LOG') && DEBUG_MYSQL_LOG) {
            $time = microtime(true);
        }
        try {
            $retry = 0;
            redo:
            $sth = $this->pdo->prepare($sql);
            $ret = false;
            try {
                $ret = $sth->execute($args);
            } catch (\Exception $exception) {
                $ret = false;
            }
            if ($ret === FALSE) {
                $err = $sth->errorInfo();
                if (isset($err[1]) && $err[1] == 2006) {
                    $this->reconnection();
                    if ($retry == 0) {
                        $retry = 1;
                        goto redo;
                    }
                }
                $this->_lastSql = Mysql::format($sql, $args);
                if (isset($err[2])) {
                    throw new MysqlException('execute sql statement error:' . $err[0] . ',' . $err[1] . ',' . $err[2], $this->_lastSql);
                } else {
                    throw new MysqlException('execute sql statement error:', $this->_lastSql);
                }
            }
            if (defined('DEBUG_MYSQL_LOG') && DEBUG_MYSQL_LOG) {
                $this->_lastSql = Mysql::format($sql, $args);
                Logger::info($this->_lastSql, microtime(true) - $time);
            }
            return $sth;
        } catch (\Exception $exception) {
            $this->_lastSql = Mysql::format($sql, $args);
            if (defined('DEBUG_MYSQL_LOG') && DEBUG_MYSQL_LOG) {
                Logger::info($this->_lastSql, microtime(true) - $time);
            }
            throw new MysqlException($exception->getMessage(), $this->_lastSql, $exception->getCode(), $exception);
        }
    }

    /**
     * 获取多行记录
     * @param string $sql
     * @param null $args
     * @param null $fetch_style
     * @param null $fetch_argument
     * @param array|null $ctor_args
     * @return array
     * @throws MysqlException
     */
    public function getList(string $sql, $args = null, $fetch_style = null, $fetch_argument = null, array $ctor_args = null)
    {
        if ($fetch_style === null) {
            $fetch_style = PDO::FETCH_ASSOC;
        }
        $stm = $this->execute($sql, $args);
        if ($fetch_style !== null && $fetch_argument !== null && $ctor_args !== null) {
            $rows = $stm->fetchAll($fetch_style, $fetch_argument, $ctor_args);
        } elseif ($fetch_style !== null && $fetch_argument !== null) {
            $rows = $stm->fetchAll($fetch_style, $fetch_argument);
        } elseif ($fetch_style !== null) {
            $rows = $stm->fetchAll($fetch_style);
        } else {
            $rows = $stm->fetchAll();
        }
        $stm->closeCursor();
        return $rows;
    }

    /**
     * 获取单行记录
     * @param string $sql
     * @param null $args
     * @param null $fetch_style
     * @param null $cursor_orientation
     * @param int $cursor_offset
     * @return mixed|null
     * @throws MysqlException
     */
    public function getRow(string $sql, $args = null, $fetch_style = null, $cursor_orientation = null, $cursor_offset = 0)
    {
        if ($fetch_style === null) {
            $fetch_style = PDO::FETCH_ASSOC;
        }
        $stm = $this->execute($sql, $args);
        $row = $stm->fetch($fetch_style, $cursor_orientation, $cursor_offset);
        $stm->closeCursor();
        return $row === false ? null : $row;
    }

    /**
     * 获得单个字段内容
     * @param string $sql
     * @param null $args
     * @param null $field
     * @return mixed|null
     * @throws MysqlException
     */
    public function getOne(string $sql, $args = null, $field = null)
    {
        $row = $this->getRow($sql, $args);
        if ($row == null) {
            return null;
        }
        if (is_string($field) && !empty($field)) {
            return isset($row[$field]) ? $row[$field] : null;
        }
        return current($row);
    }

    /**
     * 获取某个字段的最大值
     * @param string $tbname
     * @param string $field
     * @param null $where
     * @param null $args
     * @return null
     * @throws MysqlException
     */
    public function getMax(string $tbname, string $field, $where = null, $args = null)
    {
        $sql = "select max(`{$field}`) from {$tbname}";
        if ($where !== null) {
            $where = trim($where);
            if ($args != null) {
                $args = is_array($args) ? $args : [$args];
            }
            if (is_int($where) || is_numeric($where)) {
                $args = [intval($where)];
                $where = 'id=?';
            }
            $sql .= ' where ' . $where;
        }
        $row = $this->getRow($sql, $args, PDO::FETCH_NUM);
        if ($row == null) {
            return null;
        }
        return $row[0];
    }

    /**
     * 获取某个字段的最小值
     * @param string $tbname
     * @param string $field
     * @param null $where
     * @param null $args
     * @return null
     * @throws MysqlException
     */
    public function getMin(string $tbname, string $field, $where = null, $args = null)
    {
        $sql = "select min(`{$field}`) from {$tbname}";
        if ($where !== null) {
            $where = trim($where);
            if ($args != null) {
                $args = is_array($args) ? $args : [$args];
            }
            if (is_int($where) || is_numeric($where)) {
                $args = [intval($where)];
                $where = 'id=?';
            }
            $sql .= ' where ' . $where;
        }
        $row = $this->getRow($sql, $args, PDO::FETCH_NUM);
        if ($row == null) {
            return null;
        }
        return $row[0];
    }

    /**
     * 创建一个sql语句原义片段,一般用于更新 插入数据时数组的值
     * @param string $sql
     * @param null $args
     * @return SqlRaw
     */
    public function raw(string $sql, $args = null)
    {
        return new SqlRaw($sql, $args);
    }

    /**
     * 插入记录
     * @param string $tbname
     * @param array $values
     * @throws MysqlException
     */
    public function insert(string $tbname, array $values = [])
    {
        if (count($values) == 0) {
            return;
        }
        $names = [];
        $vals = [];
        $temp = [];
        foreach ($values as $key => $item) {
            $names[] = '`' . $key . '`';
            if ($item === null) {
                $vals [] = 'NULL';
            } else if ($item instanceof SqlRaw) {
                $vals [] = $item->sql;
                if (is_array($item->args)) {
                    foreach ($item->args as $it) {
                        $temp[] = $it;
                    }
                }
            } else {
                $vals [] = '?';
                $type = gettype($item);
                switch ($type) {
                    case 'bool':
                    case 'boolean':
                        $temp[] = $item ? 1 : 0;
                        break;
                    case 'int':
                    case 'integer':
                    case 'double':
                    case 'float':
                    case 'string':
                        $temp[] = $item;
                        break;
                        break;
                    case 'array':
                    case 'object':
                        $temp[] = json_encode($item, JSON_UNESCAPED_UNICODE);
                        break;
                    default :
                        $temp[] = strval($item);
                        break;
                }
            }
        }
        $sql = 'insert into ' . $tbname . '(' . join(',', $names) . ') values (' . join(',', $vals) . ')';
        $Stm = $this->execute($sql, $temp);
        $Stm->closeCursor();
    }

    /**
     * 替换记录集
     * @param string $tbname
     * @param array $values
     * @throws MysqlException
     */
    public function replace(string $tbname, array $values = [])
    {
        if (count($values) == 0) {
            return;
        }
        $names = [];
        $vals = [];
        $temp = [];
        foreach ($values as $key => $item) {
            $names[] = '`' . $key . '`';
            if ($item === null) {
                $vals [] = 'NULL';
            } else if ($item instanceof SqlRaw) {
                $vals [] = $item->sql;
                if (is_array($item->args)) {
                    foreach ($item->args as $it) {
                        $temp[] = $it;
                    }
                }
            } else {
                $vals [] = '?';
                $type = gettype($item);
                switch ($type) {
                    case 'bool':
                    case 'boolean':
                        $temp[] = $item ? 1 : 0;
                        break;
                    case 'int':
                    case 'integer':
                    case 'double':
                    case 'float':
                    case 'string':
                        $temp[] = $item;
                        break;
                        break;
                    case 'array':
                    case 'object':
                        $temp[] = json_encode($item, JSON_UNESCAPED_UNICODE);
                        break;
                    default :
                        $temp[] = strval($item);
                        break;
                }
            }
        }
        $sql = 'replace into ' . $tbname . '(' . join(',', $names) . ') values (' . join(',', $vals) . ')';
        $Stm = $this->execute($sql, $temp);
        $Stm->closeCursor();
    }

    /**
     * 更新记录集
     * @param string $tbname
     * @param array $values
     * @param null $where
     * @param null $args
     * @throws MysqlException
     */
    public function update(string $tbname, array $values, $where = null, $args = null)
    {
        if (count($values) == 0) {
            return;
        }
        $where = trim($where);
        if (is_int($where) || is_numeric($where)) {
            $args = [intval($where)];
            $where = 'id=?';
        }
        $maps = [];
        $temp = [];
        foreach ($values as $key => $item) {
            if ($item === null) {
                $maps [] = '`' . $key . '`=NULL';
            } else if ($item instanceof SqlRaw) {
                $maps [] = '`' . $key . '`=' . $item->sql;
                if (is_array($item->args)) {
                    foreach ($item->args as $it) {
                        $temp[] = $it;
                    }
                }
            } else {
                $maps [] = '`' . $key . '`=?';
                $type = gettype($item);
                switch ($type) {
                    case 'bool':
                    case 'boolean':
                        $temp[] = $item ? 1 : 0;
                        break;
                    case 'int':
                    case 'integer':
                    case 'double':
                    case 'float':
                    case 'string':
                        $temp[] = $item;
                        break;
                        break;
                    case 'array':
                    case 'object':
                        $temp[] = json_encode($item, JSON_UNESCAPED_UNICODE);
                        break;
                    default :
                        $temp[] = strval($item);
                        break;
                }
            }
        }
        $sql = 'update ' . $tbname . ' set ' . join(',', $maps);
        if (!empty($where)) {
            $sql .= ' where ' . $where;
        }
        if (is_array($args)) {
            foreach ($args as $it) {
                $temp[] = $it;
            }
        } else {
            $temp[] = $args;
        }
        $Stm = $this->execute($sql, $temp);
        $Stm->closeCursor();
    }

    /**
     * 删除记录集
     * @param string $tbname
     * @param null $where
     * @param null $args
     * @throws MysqlException
     */
    public function delete(string $tbname, $where = null, $args = null)
    {
        $where = trim($where);
        if (is_int($where) || is_numeric($where)) {
            $args = [intval($where)];
            $where = 'id=?';
        }
        $sql = 'DELETE FROM ' . $tbname;
        if (!empty($where)) {
            $sql .= ' where ' . $where;
        }
        $Stm = $this->execute($sql, $args);
        $Stm->closeCursor();
    }

    /**
     * 获取表字段
     * @param string $tbname
     * @return array
     * @throws MysqlException
     */
    public function getFields(string $tbname)
    {
        return $this->getList('show full fields from `' . $tbname . '`');
    }

    /**
     * 判断字段是否存在
     * @param string $tbname
     * @param string $field
     * @return bool
     * @throws MysqlException
     */
    public function existsField(string $tbname, string $field)
    {
        return $this->getRow('DESCRIBE `' . $tbname . '` `' . $field . '`;') !== null;
    }

    /**
     * 创建数据库表
     * @param string $tbname
     * @param array $options
     * @throws MysqlException
     */
    public function createTable(string $tbname, array $options = [])
    {
        $options = array_merge([
            'engine' => 'InnoDB',
            'charset' => 'utf8',
            'comment' => '',
        ], $options);
        if ($this->existsTable($tbname)) {
            throw new MysqlException("数据库表已经存在,{$tbname}");
        }
        $sql = "create table `{$tbname}` (`id` int(11) not null auto_increment,primary key (`id`)) engine={$options['engine']} default charset={$options['charset']} comment=?";
        $stm = $this->execute($sql, [$options['comment']]);
        if (!$stm) {
            throw new MysqlException("创建数据库表失败,{$tbname}");
        }
    }

    /**
     * 添加字段
     * @param string $tbname
     * @param string $field
     * @param array $options
     * @return int
     * @throws MysqlException
     */
    public function addField(string $tbname, string $field, array $options = [])
    {
        $options = array_merge([
            'type' => 'VARCHAR',
            'len' => 250,
            'scale' => 0,
            'def' => null,
            'comment' => '',
        ], $options);

        list($type, $len, $scale, $def, $comment) = array_values($options);
        $type = strtoupper($type);
        $sql = "ALTER TABLE {$tbname} ADD `{$field}`";
        switch ($type) {
            case 'VARCHAR':
            case 'INT':
            case 'BIGINT':
            case 'SMALLINT':
            case 'INTEGER':
            case 'TINYINT':
                $sql .= $type . '(' . $len . ')';
                break;
            case 'DECIMAL':
            case 'DOUBLE':
            case 'FLOAT':
                $sql .= $type . '(' . $len . ',' . $scale . ')';
                break;
            default:
                $sql .= $type;
                break;
        }
        $sql .= ' DEFAULT ' . Mysql::escape($def);
        if ($comment) {
            $sql .= ' COMMENT ' . Mysql::escape($comment);
        }
        $sql .= ';';
        return $this->exec($sql);
    }

    /**
     * 修改字段
     * @param string $tbname
     * @param string $field
     * @param array $options
     * @return int
     * @throws MysqlException
     */
    public function modifyField(string $tbname, string $field, array $options = [])
    {
        $chkNew = $this->existsField($tbname, $field);
        if (!$chkNew) {
            return $this->addField($tbname, $field, $options);
        }
        $options = array_merge([
            'type' => 'VARCHAR',
            'len' => 250,
            'scale' => 0,
            'def' => null,
            'comment' => '',
        ], $options);
        list($type, $len, $scale, $def, $comment) = array_values($options);
        $type = strtoupper($type);
        $sql = "ALTER TABLE {$tbname} MODIFY `{$field}`";
        switch ($type) {
            case 'VARCHAR':
            case 'INT':
            case 'BIGINT':
            case 'SMALLINT':
            case 'INTEGER':
            case 'TINYINT':
                $sql .= $type . '(' . $len . ')';
                break;
            case 'DECIMAL':
            case 'DOUBLE':
            case 'FLOAT':
                $sql .= $type . '(' . $len . ',' . $scale . ')';
                break;
            default:
                $sql .= $type;
                break;
        }
        $sql .= ' DEFAULT ' . Mysql::escape($def);
        if ($comment) {
            $sql .= ' COMMENT ' . Mysql::escape($comment);
        }
        $sql .= ';';
        return $this->exec($sql);
    }

    /**
     * 更新字段
     * @param string $tbname
     * @param string $oldfield
     * @param string $newfield
     * @param array $options
     * @return int
     * @throws MysqlException
     */
    public function updateField(string $tbname, string $oldfield, string $newfield, array $options = [])
    {
        if ($oldfield == $newfield) {
            return $this->modifyField($tbname, $newfield, $options);
        }
        $chkNew = $this->existsField($tbname, $newfield);
        if ($chkNew) {
            return $this->modifyField($tbname, $newfield, $options);
        }
        $chkOld = $this->existsField($tbname, $oldfield);
        if (!$chkOld && !$chkNew) {
            return $this->addField($tbname, $newfield, $options);
        }

        $options = array_merge([
            'type' => 'VARCHAR',
            'len' => 250,
            'scale' => 0,
            'def' => null,
            'comment' => '',
        ], $options);
        list($type, $len, $scale, $def, $comment) = array_values($options);
        $type = strtoupper($type);
        $sql = "ALTER TABLE {$tbname} CHANGE `{$oldfield}` `{$newfield}`";
        switch ($type) {
            case 'VARCHAR':
            case 'INT':
            case 'BIGINT':
            case 'SMALLINT':
            case 'INTEGER':
            case 'TINYINT':
                $sql .= $type . '(' . $len . ')';
                break;
            case 'DECIMAL':
            case 'DOUBLE':
            case 'FLOAT':
                $sql .= $type . '(' . $len . ',' . $scale . ')';
                break;
            default:
                $sql .= $type;
                break;
        }
        $sql .= ' DEFAULT ' . Mysql::escape($def);
        if ($comment) {
            $sql .= ' COMMENT ' . Mysql::escape($comment);
        }
        $sql .= ';';
        return $this->exec($sql);
    }

    /**
     * 删除字段
     * @param string $tbname
     * @param string $field
     * @return int|null
     * @throws MysqlException
     */
    public function dropField(string $tbname, string $field)
    {
        if ($this->existsField($tbname, $field)) {
            $sql = "ALTER TABLE {$tbname} DROP `{$field}`;";
            return $this->exec($sql);
        }
        return null;
    }

    /**
     * 检查表是否存在
     * @param string $tbname
     * @return bool
     * @throws MysqlException
     */
    public function existsTable(string $tbname)
    {
        $tbname = str_replace('@pf_', $this->prefix, $tbname);
        $row = $this->getRow('SHOW TABLES LIKE ?;', $tbname);
        return $row != null;
    }

    /**
     * 删除表
     * @param string $tbname
     * @return int
     * @throws MysqlException
     */
    public function dropTable(string $tbname)
    {
        $tbname = str_replace('@pf_', $this->prefix, $tbname);
        return $this->exec('DROP TABLE IF EXISTS ' . Mysql::escape($tbname) . ';');
    }
}


<?php
/**
 * Parses and verifies the class doc comment.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Greg Sherwood <gsherwood@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
 */

use Ongr\Sniffs\Helper\Comment;

/**
 * Parses and verifies the class doc comment.
 *
 * Verifies that :
 *

 *
A class doc comment exists.

 *
There is exactly one blank line before the class comment.

 *
There are no blank lines after the class comment.

 *
Short and long descriptions end with a full stop and start with capital letter.

 *
There is a blank line between descriptions.

 *

 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Greg Sherwood <gsherwood@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_ClassCommentSniff implements PHP_CodeSniffer_Sniff
{

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

    }//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)
    {

        $className = $phpcsFile->getDeclarationName($stackPtr);
        $isTestClass = preg_match('/Test$/', $className) === 1;

        $tokens = $phpcsFile->getTokens();
        $find   = PHP_CodeSniffer_Tokens::$methodPrefixes;
        $find[] = T_WHITESPACE;

        $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
        if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
            && $tokens[$commentEnd]['code'] !== T_COMMENT
        ) {
            if (!$isTestClass) {
                $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing');
            }
            return;
        }

        // Try and determine if this is a file comment instead of a class comment.
        // We assume that if this is the first comment after the open PHP tag, then
        // it is most likely a file comment instead of a class comment.
        if ($tokens[$commentEnd]['code'] === T_DOC_COMMENT_CLOSE_TAG) {
            $start = ($tokens[$commentEnd]['comment_opener'] - 1);
        } else {
            $start = $phpcsFile->findPrevious(T_COMMENT, ($commentEnd - 1), null, true);
        }

        $prev = $phpcsFile->findPrevious(T_WHITESPACE, $start, null, true);
        if ($tokens[$prev]['code'] === T_OPEN_TAG) {
            $prevOpen = $phpcsFile->findPrevious(T_OPEN_TAG, ($prev - 1));
            if ($prevOpen === false) {
                // This is a comment directly after the first open tag,
                // so probably a file comment.
                if (!$isTestClass) {
                    $phpcsFile->addError('Missing class doc comment', $stackPtr, 'Missing');
                }
                return;
            }
        }

        if ($tokens[$commentEnd]['code'] === T_COMMENT) {
            $phpcsFile->addError('You must use "/**" style comments for a class comment', $stackPtr, 'WrongStyle');
            return;
        }

        if ($tokens[$commentEnd]['line'] !== ($tokens[$stackPtr]['line'] - 1)) {
            $error = 'There must be no blank lines after the class comment';
            $phpcsFile->addError($error, $commentEnd, 'SpacingAfter');
        }

        $commentStart = $tokens[$commentEnd]['comment_opener'];
        if ($tokens[$prev]['line'] !== ($tokens[$commentStart]['line'] - 2)) {
            $error = 'There must be exactly one blank line before the class comment';
            $phpcsFile->addError($error, $commentStart, 'SpacingBefore');
        }

        //ONGR uses special tags which squiz not allow
//        foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
//            $error = '%s tag is not allowed in class comment';
//            $data  = array($tokens[$tag]['content']);
//            $phpcsFile->addWarning($error, $tag, 'TagNotAllowed', $data);
//        }

        // ONGR Validate doc comment content.
        $comment = new Comment($phpcsFile, $commentStart);
        $punctuation = ['.', '!', '?'];

        $error = 'Text must be after asterisks';
        foreach ($comment->getJunk() as $ptr) {
            $phpcsFile->addError($error, $ptr, 'MissingAsterisk');
        }

        if (!$comment->getShortDescription()) {
            $error = 'Missing short description in class comment';
            $phpcsFile->addError($error, $commentStart, 'ShortDescription');

            return;
        }

        if ($comment->getCommentStartLine() + 1 !== $comment->getShortDescriptionLine()) {
            $error = 'Short description in class comment should be in second line';
            $phpcsFile->addError($error, $comment->getShortDescriptionStartPtr(), 'ShortDescriptionLine');
        }

        if (ucfirst($comment->getShortDescription()) !== $comment->getShortDescription()) {
            $error = 'Short description must start with capital letter';
            $fix = $phpcsFile->addFixableError($error, $comment->getShortDescriptionStartPtr(), 'ShortDescriptionCapital');
            if ($fix) {
                $ptr = $comment->getShortDescriptionStartPtr();
                $phpcsFile->fixer->replaceToken(
                    $ptr,
                    ucfirst($tokens[$ptr]['content'])
                );
            }
        }

        if (!in_array(substr($comment->getShortDescription(), -1), $punctuation, true)) {
            $error = 'Short description must end with punctuation';
            $fix = $phpcsFile->addFixableError($error, $comment->getShortDescriptionEndPtr(), 'ShortDescriptionPunctuation');
            if ($fix) {
                $ptr = $comment->getShortDescriptionEndPtr();
                $phpcsFile->fixer->replaceToken(
                    $ptr,
                    $tokens[$ptr]['content'] . '.'
                );
            }
        }

        if (!$comment->getLongDescription()) {
            return;
        }

        if ($comment->getLongDescriptionLine() !== $comment->getShortDescriptionLine() + 2) {
            $error = 'Long description must be separated by 1 blank line from short description';
            $phpcsFile->addError($error, $comment->getLongDescriptionStartPtr(), 'LongDescriptionLocation');
        }

        if (!in_array(substr($comment->getLongDescription(), -1), $punctuation, true)) {
            $error = 'Long description must end with punctuation';
            $fix = $phpcsFile->addFixableError($error, $comment->getLongDescriptionEndPtr(), 'LongDescriptionPunctuation');
            if ($fix) {
                $ptr = $comment->getLongDescriptionEndPtr();
                $phpcsFile->fixer->replaceToken(
                    $ptr,
                    $tokens[$ptr]['content'] . '.'
                );
            }
        }

        if (ucfirst($comment->getLongDescription()) !== $comment->getLongDescription()) {
            $error = 'Long description must start with capital letter';
            $fix = $phpcsFile->addFixableError(
                $error,
                $comment->getLongDescriptionStartPtr(),
                'LongDescriptionCapital'
            );

            if ($fix) {
                $ptr = $comment->getLongDescriptionStartPtr();
                $phpcsFile->fixer->replaceToken(
                    $ptr,
                    ucfirst($tokens[$ptr]['content'])
                );
            }
        }
    }//end process()

}//end class


<?php

namespace Nightjar\Slug;

use LogicException;
use SilverStripe\Core\Extension;
use SilverStripe\ORM\DataObject;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse_Exception;

/**
 * This is for handling a request for a slugged DataObject, and should be applied to a Controller.
 *
 * One can either supply the method name (to get slugs from) as a string to the constructor,
 * or define a slug_trails {@see SilverStripe\Core\Config} property on the Controller.
 * The latter allows support for multiple slugged relationships to reside on the same
 * {@see SilverStripe\ORM\DataObject}, and the `slug_trails` property is an array map
 * in the format of
 * [ $Trail => $RelationshipName, ... ]
 * where $Trail is the URL segment {@see $url_handlers} it is loaded over,
 * and $RelationshipName is the internal method to call to resolve the $Slug parameter
 * of the url_handler.
 * a.k.a.
 * MyController:
 *   slug_trails:
 *     'news' => 'NewsArticles'
 */
class SlugHandler extends Extension
{
    /**
     * We must define these statically up front, so we cannot tell if the model we'd otherwise be handling has only
     * one slugged relationship, or many of them. In the former case the simple $Slug is sufficient, but if there are
     * many releationships we require $Trail in order to follow to the correct slug. So both routes are defiend.
     *
     * @var array
     */
    private static $url_handlers = [
        '$Trail/$Slug!' => 'handleSlug',
        '$Slug!' => 'handleSlug'
    ];

    private static $allowed_actions = [
        'handleSlug'
    ];

    /**
     * Holds relationship info for single slugged relationship objects, if passed to the constructor
     *
     * @var null|array
     */
    protected $slugs;

    /**
     * Which function on the Controller will get us the initial DataObject?
     *
     * @var array
     */
    protected $dataSource;

    /**
     * Apply extension to {@see SilverStripe\Control\Controller}.
     * Supply a parameter as a shorthand if handling multiple slugs on the same object is unneeded.
     * Also takes the name of the initial getter function used to move from Controller to Model,
     * the default is set to 'getFailover' as this is nicely generic - so if this property is not set
     * then a failover should be ensured. The data source parameter could even be `Me` if the controller is
     * 'bare' and getter methods exist directly on it for each slugged object type (with no parent relation).
     *
     * @param string $relationship Relationship name (optional)
     * @param string $dataSource getter function name
     */
    public function __construct($relationship = null, $dataSource = 'getFailover')
    {
        parent::__construct();
        $this->slugs = $relationship ? ['' => $relationship] : null;
        $this->dataSource = $dataSource;
    }

    /**
     * Fetches the slugged item at the end of a route (slime trail).
     *
     * @return DataObject
     * @throws LogicException
     * @throws HTTPResponse_Exception 404
     */
    protected function findSlug()
    {
        /** @var SilverStripe\Control\Controller */
        $owner = $this->getOwner();
        $request = $owner->getRequest();

        // You're probably wondering about these variable names...
        $plot = $this->dataSource;
        $garden = $owner->$plot();
        if (!$garden) {
            $mrMcGregors = get_class($owner);
            throw new LogicException("There is no garden in $mrMcGregors::$plot() to find Slugs in!");
        }
        $slime = $this->slugs;
        if (!$slime) {
            $slime = $garden->config()->get('slug_trails') ?: $owner->config()->get('slug_trails');
        }
        $trail = $request->param('Trail') ?: '';
        $slimeTrail = array_key_exists($trail, $slime) ? $slime[$trail] : $owner->httpError(404);
        $slug = $request->param('Slug');
        // ...I follow slime trails in the garden to find slugs :)
        return $garden->$slimeTrail()->find('URLSlug', $slug);
    }

    /**
     * Process a request for a slugged item.
     *
     * @param HTTPRequest $request
     * @return array customisation of owner's otherwise normal output
     */
    public function handleSlug(HTTPRequest $request)
    {
        $item = $this->findSlug();
        if (!$item) {
            $owner = $this->getOwner();
            $owner->httpError(404);
        }
        $item->setSlugActive(Slug::ACTIVE_CURRENT);
        return ['ActiveSlug' => $item];
    }
}


<?php

namespace CarbonPHP;

use CarbonPHP\Helpers\Serialized;

/**
 * Trait Singleton
 * @package Carbon
 *
 * Singeltons are considered an anti-pattern if being utilized during runtime.
 * This class is designed to give you a lot of features for the quick and dirty.
 * PHP has magic methods that allow you to do almost anything to the syntax, even
 * make it more Javascript like. Adding
 *
 * class Example {
 *      use Singleton;
 *      const Singleton = true;  // for auto class serialization
 *
 *      ...
 * }
 *
 * Will cause serializable variables defined in the class to be stored between sessions.
 * The Caveat:
 *      Instead of using the new statement you must use getInstance() in a static context.
 *      $obj = Example::getInstance();
 *
 * Javascript fans: If singleton is used, the following syntax becomes available
 *
 *      $obj->newClosure = function () { print "Closure Binding"; };
 *
 *      $obj->newClosure();
 *
 * This will store the new closure in the methods variable and attempt to run it using
 * call_user_func_array();
 *
 */

trait Singleton
{
    /**
     * @var
     */
    public $storage;                // A Temporary variable for 'quick data'
    /**
     * @var array
     */
    private $methods = array();   // Anonymous Function Declarations

    /**
     * @param $methodName
     * @param array $arguments
     * @return Singleton|mixed
     * @throws \ReflectionException
     * @throws \InvalidArgumentException
     */
    public static function __callStatic($methodName, array $arguments = array())
    {
        return self::getInstance()->Skeleton($methodName, $arguments);
    }

    /**
     * @param array ...$args
     * @return self
     * @throws \ReflectionException
     */
    public static function newInstance(...$args): self
    {   // Start a new instance of the class and pass any arguments
        self::clearInstance();
        $reflect = new \ReflectionClass($class = static::class);
        $GLOBALS['Singleton'][$class] = $reflect->newInstanceArgs($args);

        return $GLOBALS['Singleton'][$class];
    }

    /**
     * @param array ...$args
     * @return self
     * @throws \ReflectionException
     */
    public static function getInstance(...$args): self
    {   // see if the class has already been called this run
        if (!empty($GLOBALS['Singleton'][$calledClass = static::class])) {
            return $GLOBALS['Singleton'][$calledClass];
        }
        // check if the object has been sterilized in the session
        // This will invoke the __wake up operator   TODO - base64_decode
        if (isset($_SESSION[$calledClass]) && Serialized::is_serialized($_SESSION[$calledClass], $GLOBALS['Singleton'][$calledClass]))
            return $GLOBALS['Singleton'][$calledClass];

        // Start a new instance of the class and pass any arguments
        $class = new \ReflectionClass($calledClass);
        $GLOBALS['Singleton'][$calledClass] = $class->newInstanceArgs($args);

        return $GLOBALS['Singleton'][$calledClass];
    }

    /**
     * @param $object
     * @return self
     */
    public static function setInstance(self $object): self
    {
        return $GLOBALS['Singleton'][static::class] = $object;
    }

    /**
     * Remove the current instance. Also removes any serialized data.
     */
    public static function clearInstance(): void
    {
        unset($_SESSION[$calledClass = static::class], $GLOBALS['Singleton'][$calledClass]);
    }

    /**
     * @param $methodName
     * @param array $arguments
     * @return Singleton|mixed
     * @throws \InvalidArgumentException
     */
    public function __call($methodName, array $arguments = [])
    {
        return $this->Skeleton($methodName, $arguments);
    }

    /**
     * @param $methodName
     * @param array $arguments
     * @return Singleton|mixed
     * @throws \InvalidArgumentException
     * @noinspection PhpUnusedPrivateMethodInspection
     */
    private function Skeleton(string $methodName, array $arguments = [])
    {
        // Have we used addMethod() to override an existing method
        if (array_key_exists($methodName, $this->methods)) {
            return (null === ($result = \call_user_func_array($this->methods[$methodName], $arguments)) ? $this : $result);
        }
        // Is the method in the current scope ( public, protected, private ).
        // Note declaring the method as private is the only way to ensure single instancing
        if (method_exists($this, $methodName)) {
            return (null === ($result = \call_user_func_array(array($this, $methodName), $arguments)) ? $this : $result);
        }
        // Check to see if we've bound it to the class using an undefined attribute (variable)
        if (\is_callable($this->{$methodName})) {
            $this->addMethod($methodName, $this->{$methodName});
            return \call_user_func_array($this->{$methodName}, $arguments);
        }
        // closure binding
        if (array_key_exists('closures', $GLOBALS) && array_key_exists($methodName, $GLOBALS['closures'])) {
            $function = $GLOBALS['closures'][$methodName];
            $this->addMethod($methodName, $function);
            return (null === ($result = \call_user_func_array($this->methods[$methodName], $arguments)) ? $this : $result);
        }
        throw new \InvalidArgumentException("There is valid method or closure with the given name '$methodName' to call");
    }

    /** Attempt to bind a closure to the working scope. This would mean the calling class
     * not the class which uses the Singelton trait
     * @param $name
     * @param $closure
     * @noinspection PhpUnusedPrivateMethodInspection
     */
    private function addMethod(string $name, callable $closure): void
    {
        $this->methods[$name] = \Closure::bind($closure, $this, static::class);
    }

    /**
     *  If a previous class object was stored in our session then getInstance()
     *  will return the un-serialized object.
     */
    public function __wakeup()
    {
        $object = get_object_vars($this);
        foreach ($object as $item => $value) {
            Serialized::is_serialized($value, $this->$item);
        }
        if (method_exists($this, '__construct')) {
            $this->__construct();
        }
    }

    /** Attempt to serialize the current class.
     * @return array|null
     */
    public function __sleep()
    {
        if (!\defined('self::Singleton') || !self::Singleton) {
            return [];
        }

        foreach (get_object_vars($this) as $key => &$value) {
            if (empty($value)) {
                continue;
            }   // The object could be null from serialization?
            if (\is_object($value)) {
                try {
                    $this->$key = (@serialize($value));
                } catch (\Exception $e) {
                    continue;
                }                // Database object we need to catch the error thrown.
            }
            $onlyKeys[] = $key;
        }
        return $onlyKeys ?? [];
    }

    /**
     *   Attempt to serialize the current class.
     */
    public function __destruct()
    {   // We require a sleep function to be set manually for singleton to manage utilization
        if (!\defined('self::Singleton') || !self::Singleton) {
            return;
        }
        try {
            $_SESSION[__CLASS__] = @serialize($this);     // TODO - base64_encode(
        } catch (\Exception $e) {
            unset($_SESSION[__CLASS__]);
            return;
        }
    }

    /**
     * @param string $variable a global variable
     * @return mixed
     */
    public function &__get(string $variable)   // TODO - MAJOR rework, store in $storage
    {
        return $GLOBALS[$variable];
    }

    /**
     * @param string $variable variable name to set to the global scope
     * @param mixed $value
     */
    public function __set(string $variable, $value)
    {
        if (\is_callable($value)) {
            $this->{$variable} = $value->bindTo($this, $this);  // This preserves the 'normal' new attribute closure binding
        } else {
            $GLOBALS[$variable] = $value;
        }
    }

    /**
     * @param string $variable check the global scope for the given variable name
     * @return bool
     */
    public function __isset(string $variable): bool
    {
        return array_key_exists($variable, $GLOBALS);
    }

    /**
     * @param string $variable unset the variable from the global scope
     */
    public function __unset(string $variable)
    {
        unset($GLOBALS[$variable]);
    }

    /**
     * @return mixed the value(s) contained in our $storage attribute
     */
    public function __invoke()
    {
        return $this->storage;
    }

    /**
     * @param array ...$argv comma separated parameters to be set to the storage attribute
     * @return self
     */
    public function set(...$argv): self
    {
        $this->storage = $argv;
        return $this;
    }

    /**
     * @param string $variable
     * @return mixed
     */
    public function get(string $variable = null)
    {
        return ($variable === null ?
            $this->storage :
            $this->{$variable});
    }

    /**
     * @param string $variable the variable name to
     * lookup. Will return true if a variable matching the
     * given param exists and is accessible in the global or
     * current scope. This does not work for variable methods
     * added to the current scope via closures.
     * @return bool
     */
    public function has(string $variable): bool
    {
        return isset($this->$variable);
    }
}


<?php
/**
 * Created by IntelliJ IDEA.
 * User: Miles
 * Date: 7/27/17
 * Time: 10:26 PM
 *
 * This class is desigend to handle the session storage.
 * http://php.net/manual/en/function.session-set-save-handler.php
 *
 * If true is passed to the second parameter of our constructor, our
 * $_SESSION variables will be stored in the database.
 *
 */

namespace CarbonPHP;

use CarbonPHP\Helpers\Serialized;
use CarbonPHP\Tables\sessions;

class Session implements \SessionHandlerInterface
{
    /**
     * @var string - if we need to close or pause the session in the middle of execution,
     * this will persistently hold our session_id.
     */
    protected static $session_id;

    /**
     * @var $user_id - After a session is closed the session data is serialized and removed
     * from the global (accessible) scope.
     */
    protected static $user_id;

    /**
     * @var $callback - if the session is reset using the startApplication function,
     * this callable function will be executed. You can set this variable in the configuration.
     */
    protected static $callback;

    /**
     * Session constructor. This
     * @param null $ip
     * @param bool $dbStore
     * @param bool $verifySocket
     */
    public function __construct($ip = null, $dbStore = false, $verifySocket = true)
    {
        session_write_close(); //cancel the session's auto start, important

        headers_sent() or ini_set('session.use_strict_mode', 1);

        if ($ip === false) {
            die('Carbon has detected ip spoofing.');
        }

        if ($dbStore) {
            headers_sent() or ini_set('session.gc_probability', 1);  // Clear any lingering session data in default locations
            if (!session_set_save_handler($this, false)) {
                die('Session failed to store remotely');
            }
        }

        if (SOCKET && $verifySocket) {
            $this->verifySocket($ip);
        }

        if (false === @session_start()) {
            die('Carbon failed to start your session');
        }
    }

    /**
     *   Pauses the current session. This is required if you plan to fork you process and
     *   continue with session manipulation.
     */
    public static function pause()
    {
        static::$session_id = session_id();
        session_write_close();
    }

    /**
     *   After a session is stopped with session_write_close() or paused with self::pause()
     *   It maybe resumed assuming the original id was stored in self::$session_id
     */
    public static function resume()
    {
        session_id(static::$session_id);
        session_start();
    }

    /**
     * Change the callback run if self::update() is called.
     * @param callable|null $lambda
     */
    public static function updateCallback(callable $lambda = null)
    {
        self::$callback = $lambda;
    }

    /**
     * This handles our users state. If the user goes form logged-in to logged-out
     * the outer html-wrapper will be sent.
     * @param bool $clear - if true is passed serialized data will be set to null
     */
    public static function update($clear = false)
    {
        global $user;
        static $count = 0;
        $count++;

        if ($clear || !($_SESSION['id'] ?? false)) {
            Serialized::clear();
        }

        if (!\is_array($user)) {
            $user = array();
        }

        if (static::$user_id = $_SESSION['id'] = $_SESSION['id'] ?? false) {
            $_SESSION['X_PJAX_Version'] = 'v' . SITE_VERSION . 'u' . $_SESSION['id'];
        } // force reload occurs when X_PJAX_Version changes between requests

        if (!isset($_SESSION['X_PJAX_Version'])) {
            $_SESSION['X_PJAX_Version'] = SITE_VERSION;     // static::$user_id, keep this static
        }

        Request::setHeader('X-PJAX-Version: ' . $_SESSION['X_PJAX_Version']);

        /* If the session variable changes from the constant we will
         * send the full html page and notify the pjax js to reload
         * everything
         * */

        if (\is_callable(self::$callback)) {
            /** @noinspection OnlyWritesOnParameterInspection */
            ($lambda = self::$callback)($clear);    // you must have callable in a variable in fn scope
        }
        if (!\defined('X_PJAX_VERSION')) {
            \define('X_PJAX_VERSION', $_SESSION['X_PJAX_Version']);
        }
        Request::sendHeaders();  // Send any stored headers
    }

    /**
     * This will remove our session data from our scope and the database
     */
    public static function clear()
    {
        try {
            $id = session_id();
            $_SESSION = array();
            session_write_close();
            # $db = Database::database();
            # $db->prepare('DELETE FROM sessions WHERE session_id = ?')->execute([$id]);
            sessions::Delete($_SESSION,$id,[]);
            session_start();
        } catch (\PDOException $e) {
            sortDump($e);
        }
    }

    /** This function was created to make sure all socket request come from
     * an existing user who has a session stored in our database. If database
     * session storage is turned off this method will fail and exit.
     *
     * @param $ip - the ip address to look up from our database.
     */
    private function verifySocket($ip)
    {
        if ($ip) {
            $_SERVER['REMOTE_ADDR'] = $ip;
        }

        preg_match ('#PHPSESSID=([^;\s]+)#', $_SERVER['HTTP_COOKIE'] ?? false, $array, PREG_OFFSET_CAPTURE);

        $session_id = $array[1][0] ?? false;

        if (!$session_id) {
            print 'Failed to capture PHPSESSID' . PHP_EOL;
            die(1);
        }

        $db = Database::database();
        $sql = 'SELECT count(*) FROM sessions WHERE user_ip = ? AND session_id = ? LIMIT 1';
        $stmt = $db->prepare($sql);
        $stmt->execute([$_SERVER['REMOTE_ADDR'], $session_id]);
        $session = $stmt->fetchColumn();
        self::$session_id = $session_id;
        if (!$session) {
            if (SOCKET) {
                print 'BAD ADDRESS :: ' . $_SERVER['REMOTE_ADDR'] . "\n\n";
            }
            die(0);
        }
        session_id($session_id);
    }

    /** This is required for the session save handler interface.
     *  Do no change.
     *
     * @param string $savePath
     * @param string $sessionName
     * @return bool
     */
    public function open($savePath, $sessionName)
    {
        try {
            Database::database()->prepare('SELECT count(*) FROM sessions LIMIT 1')->execute();
        } catch (\PDOException $e) {
            if ($e->getCode()) {
                print "
Setting up database {$e->getCode()}
";
                Database::setUp();
                exit(1);
            }
        }
        return true;
    }

    /** Make user our session data gets stored in the db.
     * @return bool
     */
    public function close()
    {
        register_shutdown_function('session_write_close');
        return true;
    }

    /** read
     * @param string $id
     * @return string
     */
    public function read($id)
    {
        //TODO - if ip has changed and session id hasn't invalidate
        $stmt = Database::database()->prepare('SELECT session_data FROM sessions WHERE session_id = ?');
        $stmt->execute([$id]);
        return $stmt->fetchColumn() ?: '';
    }

    /** This function should never be called by you directly. It can be invoked using
     * session_write_close().
     * @param string $id
     * @param string $data
     * @return bool
     */
    public function write($id, $data)
    {
        $db = Database::database();
        if (empty(self::$user_id)) self::$user_id = $_SESSION['id'] ?? false;
        $NewDateTime = date('Y-m-d H:i:s', strtotime(date('Y-m-d H:i:s') . ' + 1 d,lay'));  // so from time of last write and whenever the gc_collector hits

        try {
            $db->prepare('REPLACE INTO sessions SET session_id = ?, user_id = UNHEX(?), user_ip = ?,  session_expires = ?, session_data = ?')->execute([
                $id, static::$user_id, IP, $NewDateTime, $data]);
        } catch (\PDOException $e) {
            sortDump($e);
        }
        return true;
    }

    /** This method can be run explicit or through
     *      session_destroy()
     * @param string $id
     * @return bool
     */
    public function destroy($id)
    {
        $db = Database::database();
        return $db->prepare('DELETE FROM sessions WHERE user_id = UNHEX(?) OR session_id = ?')->execute([self::$user_id, $id]) ?
            true : false;
    }

    /** This is our garbage collector. If a session is expired attempt to remove it.
     * This function is executed via a probability. See link for more details.
     * @link http://php.net/manual/en/features.gc.php
     * @param int $maxLife
     * @return bool
     */
    public function gc($maxLife)
    {
        $db = Database::database();
        return $db->prepare('DELETE FROM sessions WHERE (UNIX_TIMESTAMP(session_expires) + ? ) < UNIX_TIMESTAMP(?)')->execute([$maxLife, date('Y-m-d H:i:s')]) ?
            true : false;
    }
}