Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
+158
-53
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Session\Store;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -35,7 +35,7 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function loadSession( $sessionId )
|
||||
{
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( $this->_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) ) )
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( static::_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) ) )
|
||||
{
|
||||
return \IPS\Redis::i()->decode( $result['data'] );
|
||||
}
|
||||
@@ -55,8 +55,8 @@ class _Redis extends \IPS\Session\Store
|
||||
$group = \IPS\Member\Group::load( $data['member_group'] );
|
||||
|
||||
/* Update the specific session */
|
||||
\IPS\Redis::i()->delete( $this->_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->hMSet( $this->_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ), array(
|
||||
\IPS\Redis::i()->del( static::_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->hMSet( static::_key( 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) ), array(
|
||||
'member_id' => $data['member_id'],
|
||||
'member_name' => $data['member_name'],
|
||||
'seo_name' => $data['seo_name'],
|
||||
@@ -67,35 +67,35 @@ class _Redis extends \IPS\Session\Store
|
||||
), static::$ttl );
|
||||
|
||||
/* Update the list of sessions for the online list [ microtime => sessionID ] */
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_map' ), time(), 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_map' ), time(), 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
|
||||
/* Update users list */
|
||||
if ( $data['uagent_type'] == 'search' )
|
||||
{
|
||||
/* Make a unique row based on IP and user-agent to prevent multiple rows for each spider */
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_online_spiders' ), time(), md5( $data['ip_address'] . $data['browser'] ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_online_spiders' ), time(), md5( $data['ip_address'] . $data['browser'] ), static::$ttl );
|
||||
}
|
||||
else if ( $data['member_id'] )
|
||||
{
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_online_users' ), time(), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_online_users' ), time(), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ), static::$ttl );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* A guest may have one ip address but multiple devices, but we don't really need to track that */
|
||||
\IPS\Redis::i()->zAdd( $this->_key( 'session_online_guests' ), time(), md5( $data['ip_address'] ), static::$ttl );
|
||||
\IPS\Redis::i()->zAdd( static::_key( 'session_online_guests' ), time(), md5( $data['ip_address'] ), static::$ttl );
|
||||
}
|
||||
|
||||
/* Delete old items */
|
||||
if ( ! \IPS\Redis::i()->get( $this->_key( 'session_cleanup' ) ) )
|
||||
if ( ! \IPS\Redis::i()->get( static::_key( 'session_cleanup' ) ) )
|
||||
{
|
||||
/* Do a little clean up */
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_map' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_online_spiders' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_online_users' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( $this->_key( 'session_online_guests' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_map' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_spiders' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_users' ), 0, time() - static::$ttl );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_guests' ), 0, time() - static::$ttl );
|
||||
|
||||
/* And do it again in 3ish mins */
|
||||
\IPS\Redis::i()->setEx( $this->_key( 'session_cleanup' ), 180, time() );
|
||||
\IPS\Redis::i()->setEx( static::_key( 'session_cleanup' ), 180, time() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,11 +108,11 @@ class _Redis extends \IPS\Session\Store
|
||||
public function deleteSession( $sessionId )
|
||||
{
|
||||
$data = $this->loadSession( $sessionId );
|
||||
\IPS\Redis::i()->delete( $this->_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_map' ), 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_online_spiders' ), md5( $data['ip_address'] . $data['browser'] ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_online_users' ), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zDelete( $this->_key( 'session_online_guests' ), md5( $data['ip_address'] ) );
|
||||
\IPS\Redis::i()->del( static::_key( 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_map' ), 'session_id_' . md5( $sessionId . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_online_spiders' ), md5( $data['ip_address'] . $data['browser'] ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_online_users' ), $data['member_id'] . '__' . 'session_id_' . md5( $data['id'] . \IPS\Settings::i()->sql_pass ) );
|
||||
\IPS\Redis::i()->zRem( static::_key( 'session_online_guests' ), md5( $data['ip_address'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,14 +123,14 @@ class _Redis extends \IPS\Session\Store
|
||||
* @param array|NULL $keepSessionIds Array of session ids to keep [optional]
|
||||
* @return void
|
||||
*/
|
||||
public function deleteByMember( $memberId, $userAgent=NULL, $keepSessionIds=NULL )
|
||||
public function deleteByMember( int $memberId, string $userAgent=NULL, array $keepSessionIds=NULL )
|
||||
{
|
||||
if ( ! is_array( $keepSessionIds ) )
|
||||
if ( ! \is_array( $keepSessionIds ) and ! \is_null( $keepSessionIds ) )
|
||||
{
|
||||
$keepSessionIds = array( $keepSessionIds );
|
||||
}
|
||||
|
||||
$sessionMap = \IPS\Redis::i()->zRange( 'session_map', 0, -1 );
|
||||
$sessionMap = \IPS\Redis::i()->zRange( static::_key( 'session_map' ), 0, -1 );
|
||||
|
||||
foreach( $sessionMap as $index => $redisKey )
|
||||
{
|
||||
@@ -151,8 +151,8 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
$delete = false;
|
||||
}
|
||||
|
||||
if ( $keepSessionIds and in_array( $session['id'], $keepSessionIds ) )
|
||||
|
||||
if ( $keepSessionIds and \is_array( $keepSessionIds ) and \in_array( $session['id'], $keepSessionIds ) )
|
||||
{
|
||||
$delete = false;
|
||||
}
|
||||
@@ -171,7 +171,7 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function getSessionIds()
|
||||
{
|
||||
if ( $result = \IPS\Redis::i()->zRangeByScore( 'session_map', '-inf', '+inf', array( 'withscores' => false ) ) )
|
||||
if ( $result = \IPS\Redis::i()->zRangeByScore( static::_key( 'session_map' ), '-inf', '+inf', array( 'withscores' => false ) ) )
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function getLatestMemberSession( $memberId )
|
||||
{
|
||||
$redis = \IPS\Redis::i()->zRevRangeByScore( 'session_online_users', '+inf', '-inf', array('withscores' => FALSE, 'alpha' => TRUE ) );
|
||||
$redis = \IPS\Redis::i()->zRevRangeByScore( static::_key( 'session_online_users' ), '+inf', '-inf', array('withscores' => FALSE, 'alpha' => TRUE ) );
|
||||
|
||||
foreach( $redis as $data )
|
||||
{
|
||||
@@ -195,7 +195,7 @@ class _Redis extends \IPS\Session\Store
|
||||
|
||||
if ( $id == $memberId )
|
||||
{
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( $this->_key( $sessionKey ) ) )
|
||||
if ( $result = \IPS\Redis::i()->hGetAll( static::_key( $sessionKey ) ) )
|
||||
{
|
||||
return \IPS\Redis::i()->decode( $result['data'] );
|
||||
}
|
||||
@@ -214,23 +214,64 @@ class _Redis extends \IPS\Session\Store
|
||||
public static function clearSessions( $timeout )
|
||||
{
|
||||
/* Remove the public facing items. This is only called by PHP's session gc so individual sessions do not need removing as they are cleaned by Redis' TTL */
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_map', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_online_spiders', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_online_users', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( 'session_online_guests', 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_map' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_spiders' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_users' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->zRemRangeByScore( static::_key( 'session_online_guests' ), 0, time() - $timeout );
|
||||
\IPS\Redis::i()->del( static::_key( 'session_onlinelist' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis key
|
||||
*/
|
||||
protected static $_redisKey;
|
||||
|
||||
/**
|
||||
* Returns a key to be stored with Redis
|
||||
*
|
||||
* @param string $key Key suffix
|
||||
* @return string
|
||||
*/
|
||||
protected function _key( $key )
|
||||
protected static function _key( $key )
|
||||
{
|
||||
return $key;
|
||||
/* Only manage the session_onlist key which is prone to corruption. We don't want to wipe out the online list each time this file fails */
|
||||
if ( $key == 'session_onlinelist' )
|
||||
{
|
||||
if ( !static::$_redisKey )
|
||||
{
|
||||
/* Last access ensures that the data is not stale if we fail back to MySQL and then go back to Redis later */
|
||||
if ( !( static::$_redisKey = \IPS\Redis::i()->get( 'redisKey_session' ) ) OR ! \IPS\Redis::i()->get( 'redisStore_lastAccess' ) )
|
||||
{
|
||||
static::$_redisKey = md5( mt_rand() );
|
||||
\IPS\Redis::i()->setex( 'redisKey_session', 604800, static::$_redisKey );
|
||||
\IPS\Redis::i()->setex( 'redisStore_lastAccess', ( 3 * 3600 ), time() );
|
||||
}
|
||||
}
|
||||
|
||||
return static::$_redisKey . '_' . $key;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resets the session key to force ignore any previous redis data stored
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function resetKey()
|
||||
{
|
||||
static::$_redisKey = md5( mt_rand() );
|
||||
\IPS\Redis::i()->setex( 'redisKey_session', 604800, static::$_redisKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis key
|
||||
*/
|
||||
protected $fetchAttempt = 0;
|
||||
|
||||
/**
|
||||
* Fetch all online users (but not spiders)
|
||||
*
|
||||
@@ -243,36 +284,83 @@ class _Redis extends \IPS\Session\Store
|
||||
*/
|
||||
public function getOnlineUsers( $flags=0, $sort='desc', $limit=NULL, $memberGroup=NULL, $showAnonymous=FALSE )
|
||||
{
|
||||
$results = \IPS\Redis::i()->lRange('session_onlinelist', 0, -1 );
|
||||
|
||||
try
|
||||
{
|
||||
$results = \IPS\Redis::i()->lRange( static::_key( 'session_onlinelist' ), 0, -1 );
|
||||
}
|
||||
catch ( \RedisException $e )
|
||||
{
|
||||
/* Something went wrong, so reset the key to force a new file */
|
||||
static::resetKey();
|
||||
|
||||
$results = NULL;
|
||||
}
|
||||
|
||||
if ( ! $results )
|
||||
{
|
||||
/* Ensure file is deleted */
|
||||
try
|
||||
{
|
||||
\IPS\Redis::i()->del( static::_key( 'session_onlinelist' ) );
|
||||
}
|
||||
catch ( \RedisException $e )
|
||||
{
|
||||
/* Something went wrong, so reset the key to force a new file */
|
||||
static::resetKey();
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'sort' => $sort === 'asc' ? 'asc' : 'desc',
|
||||
'store' => \IPS\Redis::i()->prefix . 'session_onlinelist',
|
||||
'store' => \IPS\Redis::i()->prefix . static::_key( 'session_onlinelist' ),
|
||||
'alpha' => true,
|
||||
'by' => 'nosort ' . $sort === 'asc' ? 'asc' : 'desc',
|
||||
'ttl' => 30, /* This ensures the stored file session_online only lasts for 30 seconds */
|
||||
'get' => array(
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->member_id',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->member_name',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->seo_name',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->member_group',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->login_type',
|
||||
$this->_key( \IPS\Redis::i()->prefix ) . '*->data'
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->member_id',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->member_name',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->seo_name',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->member_group',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->login_type',
|
||||
static::_key( \IPS\Redis::i()->prefix ) . '*->data'
|
||||
)
|
||||
);
|
||||
|
||||
\IPS\Redis::i()->sort( $this->_key( 'session_map' ), $options );
|
||||
\IPS\Redis::i()->sort( static::_key( 'session_map' ), $options );
|
||||
|
||||
$results = \IPS\Redis::i()->lRange('session_onlinelist', 0, -1 );
|
||||
try
|
||||
{
|
||||
/* Force expiration in 30 seconds to prevent stale caches hanging around */
|
||||
\IPS\Redis::i()->expire( static::_key( 'session_onlinelist' ), 30 );
|
||||
|
||||
$results = \IPS\Redis::i()->lRange( static::_key( 'session_onlinelist' ), 0, -1 );
|
||||
}
|
||||
catch ( \RedisException $e )
|
||||
{
|
||||
$this->fetchAttempt++;
|
||||
|
||||
/* Something went wrong, so reset the key to force a new file */
|
||||
static::resetKey();
|
||||
|
||||
if ( $this->fetchAttempt < 2 )
|
||||
{
|
||||
/* And try again, but only once more to prevent an infinite loop */
|
||||
return $this->getOnlineUsers( $flags, $sort, $limit, $memberGroup, $showAnonymous );
|
||||
}
|
||||
else
|
||||
{
|
||||
$results = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the fetch attempt */
|
||||
$this->fetchAttempt = 0;
|
||||
|
||||
/* Sort returns a flat array, so [ 1, matt, matt, 4, 0, 2, Joe, joe, 3, 0 .. ] so we need to build that into an associative array we can work with */
|
||||
$return = array();
|
||||
$i = 0;
|
||||
|
||||
while( $i < count( $results ) )
|
||||
while( $i < \count( $results ) )
|
||||
{
|
||||
$fields = array();
|
||||
$data = NULL;
|
||||
@@ -282,13 +370,18 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
$data = \IPS\Redis::i()->decode( $results[ $i++ ] );
|
||||
}
|
||||
/* login_type must be cast as an integer or else anonymous state can be lost when adjustSessions() runs */
|
||||
elseif( $field === 'login_type' )
|
||||
{
|
||||
$fields[ $field ] = (int) $results[ $i++ ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$fields[ $field ] = $results[ $i++ ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_array( $data ) AND count( $data ) )
|
||||
if ( \is_array( $data ) AND \count( $data ) )
|
||||
{
|
||||
/* Have we already fetched this member? */
|
||||
if ( $fields['member_id'] and isset( $return[ $fields['member_id'] ] ) )
|
||||
@@ -301,7 +394,13 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Ignore spiders */
|
||||
if ( ! $fields['member_id'] and ! ( $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_GUEST or $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_INCOMPLETE ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[ $fields['member_id'] ? $fields['member_id'] : $data['ip_address'] ] = array_merge( $data, $fields );
|
||||
}
|
||||
}
|
||||
@@ -333,7 +432,7 @@ class _Redis extends \IPS\Session\Store
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $showAnonymous and $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS )
|
||||
if ( ! $showAnonymous and $data['login_type'] == \IPS\Session\Front::LOGIN_TYPE_ANONYMOUS and ( !\IPS\Member::loggedIn()->member_id OR $data['member_id'] != \IPS\Member::loggedIn()->member_id ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -344,12 +443,18 @@ class _Redis extends \IPS\Session\Store
|
||||
/* Count only? */
|
||||
if ( $flags & static::ONLINE_COUNT_ONLY )
|
||||
{
|
||||
return count( $members );
|
||||
return \count( $members );
|
||||
}
|
||||
|
||||
/* Hooray for PHP 7 */
|
||||
usort($members, function ($m1, $m2 )
|
||||
{
|
||||
return $m2['running_time'] <=> $m1['running_time'];
|
||||
} );
|
||||
|
||||
if ( $limit )
|
||||
{
|
||||
return array_slice( $members, $limit[0], $limit[1], TRUE );
|
||||
return \array_slice( $members, $limit[0], $limit[1], TRUE );
|
||||
}
|
||||
|
||||
return $members;
|
||||
@@ -372,9 +477,9 @@ class _Redis extends \IPS\Session\Store
|
||||
{
|
||||
$members = array();
|
||||
|
||||
foreach( $this->getOnlineUsers( 0, 'desc' ) as $member )
|
||||
foreach( $this->getOnlineUsers( static::ONLINE_MEMBERS, 'desc' ) as $member )
|
||||
{
|
||||
if ( $member['current_appcomponent'] == $app and $member['current_module'] == $module and $member['current_controller'] = $controller and $member['current_id'] = $id )
|
||||
if ( $member['current_appcomponent'] == $app and $member['current_module'] == $module and $member['current_controller'] == $controller and $member['current_id'] == $id )
|
||||
{
|
||||
if ( $url and mb_stristr( $member['location_url'], $url ) )
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user