Seditio Source
Root |
./othercms/ips_4.3.4/admin/convertutf8/system/Session/Session.php
<?php
/**
 * @brief        Session Handler
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @license        https://www.invisioncommunity.com/legal/standards/
 * @package        Invision Community
 * @since        6th Sept 2013
 */

namespace IPSUtf8;

/**
 * Session Handler
 */
class Session
{
   
/**
     * @brief    Tables
     */
   
public $tables = NULL;
   
   
/**
     * @brief    Singleton Instance
     */
   
protected static $instance = NULL;
   
   
/**
     * @brief  Database Table Columns
     */
   
protected static $tableTableColumns = array(
        array(
           
'name'    => 'table_name',
           
'type'    => 'TEXT',
           
'length'  => false,
           
'null'    => false,
           
'default' => null
       
),
        array(
           
'name'    => 'table_schema',
           
'type'    => 'MEDIUMTEXT',
           
'length'  => false,
           
'null'    => true,
           
'default' => null
       
)
    );
   
   
/**
     * @brief  Database Table Columns
     */
   
protected static $sessionTableColumns = array(
        array(
           
'name'    => 'session_start',
           
'type'    => 'INT',
           
'length'  => 10,
           
'null'    => false,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_updated',
           
'type'    => 'INT',
           
'length'  => 10,
           
'null'    => false,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_status',
           
'type'    => 'VARCHAR',
           
'length'  => 255,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_current_charset',
           
'type'    => 'VARCHAR',
           
'length'  => 255,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_current_table',
           
'type'    => 'VARCHAR',
           
'length'  => 255,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_current_pkey',
           
'type'    => 'VARCHAR',
           
'length'  => 255,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_current_row',
           
'type'    => 'VARCHAR',
           
'length'  => 255,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_completed_json',
           
'type'    => 'MEDIUMTEXT',
           
'length'  => false,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_json',
           
'type'    => 'MEDIUMTEXT',
           
'length'  => false,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_has_archive',
           
'type'    => 'INT',
           
'length'  => 1,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_processing_archive',
           
'type'    => 'INT',
           
'length'  => 1,
           
'null'    => true,
           
'default' => 0
       
),
        array(
           
'name'    => 'session_is_ipb',
           
'type'    => 'INT',
           
'length'  => 1,
           
'null'    => true,
           
'default' => 0
       
),
     );

   
/**
     * @brief    Data Store
     */
   
protected $_data = NULL;
       
   
/**
     * @brief    Changed Columns
     */
   
public $changed = array();

   
/**
     * Get instance
     *
     * @return    Output
     */
   
public static function i()
    {
        if (
self::$instance === NULL )
        {
           
self::$instance = new self();
           
self::$instance->init();
        }
       
        return
self::$instance;
    }
   
   
/**
     * Init
     *
     * @return    void
     */
   
public function init()
    {
       
/* Check to see if the session table exists */
       
if ( file_exists( ROOT_PATH . '/conf_global.php' ) )
        {
            require(
ROOT_PATH . '/conf_global.php' );
        }
       
       
$setDefault = false;
       
        if ( ! \
IPSUtf8\Db::i('utf8')->checkForTable( 'convert_session_tables' ) )
        {
            \
IPSUtf8\Db::i('utf8')->createTable( array(
                 
'name'    => 'convert_session_tables',
                 
'columns' => static::$tableTableColumns
             
) );
             
             
$setDefault = true;
        }

        if ( ! \
IPSUtf8\Db::i('utf8')->checkForTable( 'convert_session' ) )
        {
            \
IPSUtf8\Db::i('utf8')->createTable( array(
                 
'name'    => 'convert_session',
                 
'columns' => static::$sessionTableColumns
             
) );
             
             
$setDefault = true;
        }
        else
        {
            try
            {
               
$this->_data = \IPSUtf8\Db::i('utf8')->select( '*', 'convert_session' )->first();
           
                if ( ! isset(
$this->_data['session_json'] ) )
                {
                   
$setDefault = true;
                }
            }
            catch( \
UnderflowException $ex )
            {
               
$setDefault = true;
            }
        }
       
        if (
$setDefault === true )
        {
           
$this->reset();
        }
       
       
/* Populate tables */
       
foreach( \IPSUtf8\Db::i('utf8')->select( '*', 'convert_session_tables' ) as $row )
        {
           
$this->tables[ $row['table_name'] ] = json_decode( $row['table_schema'], true );
        }
    }
   
   
/**
     * Reset the session and conversion process
     */
   
public function reset()
    {
       
$charSet    = null;
       
$ipbVersion = null;
   
       
/* Is this an IPB? */
       
if ( \IPSUtf8\Db::i()->checkForTable('core_applications' ) )
        {
            try
            {
               
$row = \IPSUtf8\Db::i()->select( '*', 'core_applications', array( 'app_directory=?', 'core' ) )->first();
               
                if ( ! empty(
$row['app_directory'] ) )
                {
                   
$ipbVersion = $row['app_long_version'];
                }
            }
            catch( \
UnderflowException $ex ) { }
        }
   
        if ( !
SOURCE_CHARSET_OVERRIDE )
        {
            if (
$ipbVersion >= 40000 )
            {
               
/* >= 4.0 is UTF-8 */
               
$charSet = 'utf-8';
            }
            else if (
$ipbVersion !== null )
            {
               
/* Attempt to grab current CHARSET if this is an IPB 3 */
               
if ( \IPSUtf8\Db::i()->checkForTable('core_sys_conf_settings' ) )
                {
                    try
                    {
                       
$row = \IPSUtf8\Db::i()->select( '*', 'core_sys_conf_settings', array( 'conf_key=?', 'gb_char_set' ) )->first();
                       
                        if ( ! empty(
$row['conf_key'] ) )
                        {
                           
$charSet = ( $row['conf_value'] ) ? $row['conf_value'] : $row['conf_default'];
                        }
                    }
                    catch( \
UnderflowException $ex ) { }
                }
            }
        }
        else
        {
           
$charSet = strtolower( SOURCE_CHARSET_OVERRIDE );
        }

       
$tableData = $this->_getTables();
       
        \
IPSUtf8\Db::i('utf8')->delete( 'convert_session_tables' );
       
        foreach(
$tableData['tables'] as $name => $schema )
        {
            \
IPSUtf8\Db::i('utf8')->insert( 'convert_session_tables', array(
               
'table_name'   => $name,
               
'table_schema' => json_encode( $schema )
            ) );
        }
       
       
$this->_data = array(
           
'session_start'                => time(),
           
'session_updated'              => time(),
             
'session_status'               => null,
             
'session_current_charset'    => strtolower( $charSet ),
             
'session_current_table'      => null,
             
'session_current_pkey'       => null,
             
'session_current_row'         => 0,
             
'session_completed_json'     => json_encode( array() ),
             
'session_json'               => json_encode( $tableData['data'] ),
             
'session_has_archive'         => 0,
             
'session_processing_archive' => 0,
             
'session_is_ipb'             => $ipbVersion === null ? false : true
             
         
);
         
       
/* Change database collation */
       
require( ROOT_PATH . '/conf_global.php' );
       
        if ( ! empty(
$INFO['archive_remote_sql_host'] ) AND ! empty( $INFO['archive_remote_sql_database'] ) AND ! empty( $INFO['archive_remote_sql_user'] ) )
        {
           
$this->_data['session_has_archive'] = 1;
        }
   
         \
IPSUtf8\Db::i('utf8')->delete( 'convert_session' );
        \
IPSUtf8\Db::i('utf8')->insert( 'convert_session', $this->_data );
    }
   
   
/**
     * Update 'all_tables'
     *
     * @return void
     */
   
public function updateTableData()
    {
       
$tableData = $this->_getTables();
       
        \
IPSUtf8\Db::i('utf8')->delete( 'convert_session_tables' );
       
        foreach(
$tableData['tables'] as $name => $schema )
        {
            \
IPSUtf8\Db::i('utf8')->insert( 'convert_session_tables', array(
               
'table_name'   => $name,
               
'table_schema' => json_encode( $schema )
            ) );
        }
       
       
/* Populate tables */
       
foreach( \IPSUtf8\Db::i('utf8')->select( '*', 'convert_session_tables' ) as $row )
        {
           
$this->tables[ $row['table_name'] ] = json_decode( $row['table_schema'], true );
        }
       
       
$this->save();
    }
   
   
/**
     * Grab all tables in the database that we're going to convert
     *
     * @return array
     */
   
protected function _getTables()
    {
       
/* Grab all tables to convert */
       
$stmt = \IPSUtf8\Db::i()->prepare( "SHOW TABLES" );
       
$stmt->execute();
       
$stmt->bind_result( $tableName );
       
       
$tables    = array();
       
$allTables = array();
       
$data      = array( 'version' => \IPSUtf8\Convert::VERSION_ID, 'tableCount' => 0, 'totalCount' => 0, 'convertedCount' => 0, 'charSets' => array() );
       
        if (
defined( 'FORCE_CONVERT' ) AND FORCE_CONVERT === TRUE )
        {
           
$data['force_conversion'] = 1;
        }
       
        while (
$stmt->fetch() === true )
        {
           
/* Skip x_utf_ prefixed tables in case we don't have a prefix set */
           
if ( mb_substr( $tableName, 0, 6 ) === 'x_utf_' )
            {
                continue;
            }
           
            if (
mb_substr( $tableName, 0, 5 ) === 'orig_' )
            {
                continue;
            }

           
/* Skip if it's an _old or _utftemp table */
           
if( mb_substr( $tableName, -4 ) == '_old' )
            {
                continue;
            }

            if(
mb_substr( $tableName, -8 ) == '_utftemp' )
            {
                continue;
            }
           
            if (
mb_substr( $tableName, 0, mb_strlen( \IPSUtf8\Db::i()->prefix ) ) === \IPSUtf8\Db::i()->prefix )
            {
               
$tableNameNoPrefix = $tableName;
               
                if ( \
IPSUtf8\Db::i()->prefix )
                {
                   
$tableNameNoPrefix = mb_substr( $tableName, mb_strlen( \IPSUtf8\Db::i()->prefix ) );
                }
               
               
$tables[] = $tableNameNoPrefix;
            }
        }
       
        foreach(
$tables as $table )
        {
           
/* Get count */
           
$row   = \IPSUtf8\Db::i()->query( "SELECT COUNT(*) as count FROM `" . \IPSUtf8\Db::i()->prefix . "{$table}`" )->fetch_assoc();
           
$count = $row['count'];
           
           
$data['totalCount'] += $count;
           
$data['tableCount']++;
           
           
$row = \IPSUtf8\Db::i()->query( "SHOW CREATE TABLE `" . \IPSUtf8\Db::i()->prefix . "{$table}`" )->fetch_assoc();
               
            if (
preg_match( '#\scharset=([a-z0-9]+?)(\s|$)#i', $row['Create Table'], $matches ) )
            {
               
$tblCharset = $matches[1];
               
               
$data['charSets'][ mb_strtolower( $tblCharset ) ][] = $table;
            }
            else
            {
               
$row = \IPSUtf8\Db::i()->query( "SHOW TABLE STATUS WHERE Name='" . \IPSUtf8\Db::i()->prefix . "{$table}'" )->fetch_assoc();
               
                if ( isset(
$row['Collation'] ) )
                {
                   
$tblCharset = mb_substr( $row['Collation'], 0, strpos( $row['Collation'], '_' ) );
                   
                   
$data['charSets'][ mb_strtolower( $tblCharset ) ][] = $table;
                }
                else
                {
                   
$data['charSets'][ mb_strtolower( \IPSUtf8\Convert::i()->database_charset ) ][] = $table;
                }
            }
           
           
/* Make sure we can JSON Encode this table... MySQL Comments with UTF8 characters can cause it to fail */
           
$definition = \IPSUtf8\Db::i()->getTableDefinition( $table );
           
            try
            {
               
$test = @json_encode( $definition );
               
                if (
json_last_error() != JSON_ERROR_NONE )
                {
                    throw new \
ErrorException;
                }
            }
            catch( \
ErrorException $e )
            {
               
$definition = static::arrayWalkRecursive( $definition, function( $value ) { return utf8_encode( $value ); } );
               
               
/* Try it again - if it fails this time, remove the column comments as that is likely causing the issue if they have non-latin characters */
               
$test = @json_encode( $definition );
               
                if (
json_last_error() != JSON_ERROR_NONE )
                {
                    foreach(
$definition['columns'] AS $column => $data )
                    {
                        if ( isset(
$data['comment'] ) )
                        {
                            unset(
$definition['columns'][ $column ]['comment'] );
                        }
                    }
                }
            }
           
           
/* If the collation is missing, let's do a table status check and see if we can figure it out */
           
if ( empty( $definition['collation'] ) )
            {
               
$status = \IPSUtf8\Db::i()->query( "SHOW TABLE STATUS WHERE Name='" . \IPSUtf8\Db::i()->prefix . "{$table}'" )->fetch_assoc();
               
                if( isset(
$status['Collation'] ) )
                {
                   
$definition['collation'] = $status['Collation'];
                }
            }
           
           
/* Get other data */
           
$allTables[ $table ] = array(
               
'name'       => $table,
               
'definition' => $definition,
               
'count'      => $count,
               
'charset'    => $tblCharset
           
);
        }
       
        return array(
'tables' => $allTables, 'data' => $data );
    }
   
   
/**
     * Save the state of the conversion
     */
   
public function __destruct()
    {
       
$this->save();
    }
   
   
/**
     * Get value from data store
     *
     * @param    mixed    $key    Key
     * @return    mixed    Value from the datastore
     */
   
public function __get( $key )
    {
        if (
mb_substr( $key, 0, 8 ) != 'session_' )
        {
           
$key = 'session_' . $key;
        }
       
        if( isset(
$this->_data[ $key ] ) )
        {
            if (
mb_substr( $key, -5 ) == '_json' AND ! is_array( $this->_data[ $key ] ) )
            {
               
$this->_data[ $key ] = json_decode( $this->_data[ $key ], true );
            }
           
            return
$this->_data[ $key ];
        }
               
        return
NULL;
    }
   
   
/**
     * Magic isset method
     *
     * @param    string    $key    Key
     */
   
public function __isset( $key )
    {
        if (
mb_substr( $key, 0, 8 ) != 'session_' )
        {
           
$key = 'session_' . $key;
        }
       
        return isset(
$this->_data[ $key ] );
    }
   
   
/**
     * Set value in data store
     *
     * @param    mixed    $key    Key
     * @param    mixed    $value    Value
     * @return    void
     */
   
public function __set( $key, $value )
    {
        if (
mb_substr( $key, 0, 8 ) != 'session_' )
        {
           
$key = 'session_' . $key;
        }
       
        if(
array_key_exists( $key, $this->_data ) )
        {
           
$this->_data[ $key ] = $value;
           
$this->changed[ $key ] = $value;
        }
    }
   
   
/**
     * Time taken for conversion
     *
     * @param    bool    $formatted        Return the time formatted or in seconds
     * @return    mixed
     */
   
public function timeTaken( $formatted=false )
    {
       
$seconds = ( $this->_data['session_updated'] - $this->_data['session_start'] );
       
        if (
$formatted )
        {
           
$s = $seconds % 60;
           
$m = floor( ( $seconds % 3600 ) / 60 );
           
$h = floor( ( $seconds % 86400 ) / 3600 );
           
            if (
$h )
            {
                return
"{$h} hour(s), {$m} minute(s) and {$s} seconds.";
            }
            else
            {
                return
"{$m} minute(s) and {$s} seconds.";
            }
        }
        else
        {
            return
$seconds;
        }
    }
   
   
/**
     * Save Changed Columns
     *
     * @return    void
     */
   
public function save()
    {
       
$this->_data['session_updated'] = time();
       
$insert = array( 'session_updated' => time() );
       
        if ( ! empty(
$this->changed ) )
        {            
           
/* JSON encode if required */
           
foreach ( array_merge( $this->_data, $this->changed ) as $k => $v )
            {
               
$insert[ $k ] = ( mb_substr( $k, -5 ) == '_json' AND is_array( $v ) ) ? json_encode( $v, true ) : $v;
            }
        }
        else
        {
            foreach (
$this->_data as $k => $v )
            {
               
$insert[ $k ] = ( mb_substr( $k, -5 ) == '_json' AND is_array( $v ) ) ? json_encode( $v, true ) : $v;
            }
        }
       
       
/* Save */
       
if ( count( $insert ) == count( static::$sessionTableColumns ) )
        {
            \
IPSUtf8\Db::i('utf8')->update( 'convert_session', $insert );
        }
                   
       
/* Reset our log of what's changed */
       
$this->changed = array();
       
    }

   
/**
     * Recursively apply a callback to an array - array_walk_recursive does not recurse into sub-sub-arrays, so we need a custom method
     *
     * @param    array        $array        The array
     * @param    callback    $callback    The callback
     * @return    array    The filtered array.
     */
   
public function arrayWalkRecursive( $array, $callback )
    {
        if ( !
is_array( $array ) )
        {
           
trigger_error( "\$array is not an array in \IPSUtf8\Session::arrayWalkRecursive()", E_USER_ERROR );
        }
       
        foreach(
$array AS $key => $value )
        {
            if (
is_array( $array[$key] ) )
            {
               
$array[$key] = static::arrayWalkRecursive( $array[$key], $callback );
            }
            else
            {
               
$array[$key] = $callback( $value );
            }
        }
       
        return
$array;
    }
}