Version 4.0.13.1
This commit is contained in:
1 parent
f9e857d255
commit
c6a86f0d9b
7517 files changed
+597400
-668041
No files matched your search
@@ -0,0 +1,543 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Active Record Pattern
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @since 18 Feb 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Patterns;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Active Record Pattern
|
||||
*/
|
||||
abstract class _ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = '';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database table
|
||||
* @note This MUST be over-ridden
|
||||
*/
|
||||
public static $databaseTable = '';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array();
|
||||
|
||||
/**
|
||||
* @brief Bitwise keys
|
||||
*/
|
||||
protected static $bitOptions = array();
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
* @note This needs to be declared in any child classes as well, only declaring here for editor code-complete/error-check functionality
|
||||
*/
|
||||
protected static $multitons = array();
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Connection
|
||||
*/
|
||||
public static function db()
|
||||
{
|
||||
return \IPS\Db::i();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Record
|
||||
*
|
||||
* @see \IPS\Db::build
|
||||
* @param int|string $id ID
|
||||
* @param string $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details)
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
public static function load( $id, $idField=NULL, $extraWhereClause=NULL )
|
||||
{
|
||||
/* If we didn't specify an ID field, assume the default */
|
||||
if( $idField === NULL )
|
||||
{
|
||||
$idField = static::$databasePrefix . static::$databaseColumnId;
|
||||
}
|
||||
|
||||
/* If we did, check it's valid */
|
||||
elseif( !in_array( $idField, static::$databaseIdFields ) )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
/* Does that exist in the multiton store? */
|
||||
if( $idField === static::$databasePrefix . static::$databaseColumnId and !empty( static::$multitons[ $id ] ) )
|
||||
{
|
||||
return static::$multitons[ $id ];
|
||||
}
|
||||
|
||||
/* If not, find it */
|
||||
else
|
||||
{
|
||||
/* Load it */
|
||||
try
|
||||
{
|
||||
$row = static::constructLoadQuery( $id, $idField, $extraWhereClause )->first();
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
/* If it doesn't exist in the multiton store, set it */
|
||||
if( !isset( static::$multitons[ $row[ static::$databasePrefix . static::$databaseColumnId ] ] ) )
|
||||
{
|
||||
static::$multitons[ $row[ static::$databasePrefix . static::$databaseColumnId ] ] = static::constructFromData( $row );
|
||||
}
|
||||
|
||||
/* And return it */
|
||||
return static::$multitons[ $row[ static::$databasePrefix . static::$databaseColumnId ] ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load record based on a URL
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL to load from
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
public static function loadFromUrl( \IPS\Http\Url $url )
|
||||
{
|
||||
$qs = array_merge( $url->queryString, $url->getFriendlyUrlData() );
|
||||
|
||||
if ( isset( $qs['id'] ) )
|
||||
{
|
||||
return static::load( $qs['id'] );
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct Load Query
|
||||
*
|
||||
* @param int|string $id ID
|
||||
* @param string $idField The database column that the $id parameter pertains to
|
||||
* @param mixed $extraWhereClause Additional where clause(s)
|
||||
* @return \IPS\Db\Select
|
||||
*/
|
||||
protected static function constructLoadQuery( $id, $idField, $extraWhereClause )
|
||||
{
|
||||
$where = array( array( '`' . $idField . '`=?', $id ) );
|
||||
if( $extraWhereClause !== NULL )
|
||||
{
|
||||
if ( !is_array( $extraWhereClause ) or !is_array( $extraWhereClause[0] ) )
|
||||
{
|
||||
$extraWhereClause = array( $extraWhereClause );
|
||||
}
|
||||
$where = array_merge( $where, $extraWhereClause );
|
||||
}
|
||||
|
||||
return static::db()->select( '*', static::$databaseTable, $where );
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct ActiveRecord from database row
|
||||
*
|
||||
* @param array $data Row from database table
|
||||
* @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there?
|
||||
* @return static
|
||||
*/
|
||||
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
|
||||
{
|
||||
/* Does that exist in the multiton store? */
|
||||
$obj = NULL;
|
||||
if ( isset( static::$databaseColumnId ) )
|
||||
{
|
||||
$idField = static::$databasePrefix . static::$databaseColumnId;
|
||||
$id = $data[ $idField ];
|
||||
|
||||
if( isset( static::$multitons[ $id ] ) )
|
||||
{
|
||||
if ( !$updateMultitonStoreIfExists )
|
||||
{
|
||||
return static::$multitons[ $id ];
|
||||
}
|
||||
$obj = static::$multitons[ $id ];
|
||||
}
|
||||
}
|
||||
|
||||
/* Initiate an object */
|
||||
if ( !$obj )
|
||||
{
|
||||
$classname = get_called_class();
|
||||
$obj = new $classname;
|
||||
$obj->_new = FALSE;
|
||||
$obj->_data = array();
|
||||
}
|
||||
|
||||
/* Import data */
|
||||
foreach ( $data as $k => $v )
|
||||
{
|
||||
if( static::$databasePrefix AND mb_strpos( $k, static::$databasePrefix ) === 0 )
|
||||
{
|
||||
$k = \substr( $k, \strlen( static::$databasePrefix ) );
|
||||
}
|
||||
|
||||
$obj->_data[ $k ] = $v;
|
||||
}
|
||||
|
||||
$obj->changed = array();
|
||||
|
||||
/* Init */
|
||||
if ( method_exists( $obj, 'init' ) )
|
||||
{
|
||||
$obj->init();
|
||||
}
|
||||
|
||||
/* If it doesn't exist in the multiton store, set it */
|
||||
if( isset( static::$databaseColumnId ) and !isset( static::$multitons[ $id ] ) )
|
||||
{
|
||||
static::$multitons[ $id ] = $obj;
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get which IDs are already loaded
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function multitonIds()
|
||||
{
|
||||
if ( is_array( static::$multitons ) )
|
||||
{
|
||||
return array_keys( static::$multitons );
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Data Store
|
||||
*/
|
||||
protected $_data = array();
|
||||
|
||||
/**
|
||||
* @brief Is new record?
|
||||
*/
|
||||
protected $_new = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Changed Columns
|
||||
*/
|
||||
public $changed = array();
|
||||
|
||||
/**
|
||||
* Constructor - Create a blank object with default values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setDefaultValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Default Values (overriding $defaultValues)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setDefaultValues()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from data store
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @return mixed Value from the datastore
|
||||
*/
|
||||
public function __get( $key )
|
||||
{
|
||||
if( method_exists( $this, 'get_'.$key ) )
|
||||
{
|
||||
return call_user_func( array( $this, 'get_'.$key ) );
|
||||
}
|
||||
elseif( isset( $this->_data[ $key ] ) or isset( static::$bitOptions[ $key ] ) )
|
||||
{
|
||||
if ( isset( static::$bitOptions[ $key ] ) )
|
||||
{
|
||||
if ( !isset( $this->_data[ $key ] ) or !( $this->_data[ $key ] instanceof Bitwise ) )
|
||||
{
|
||||
$values = array();
|
||||
foreach ( static::$bitOptions[ $key ] as $k => $map )
|
||||
{
|
||||
$values[ $k ] = isset( $this->_data[ $k ] ) ? $this->_data[ $k ] : 0;
|
||||
}
|
||||
$this->_data[ $key ] = new Bitwise( $values, static::$bitOptions[ $key ], method_exists( $this, "setBitwise_{$key}" ) ? array( $this, "setBitwise_{$key}" ) : NULL );
|
||||
}
|
||||
}
|
||||
return $this->_data[ $key ];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value in data store
|
||||
*
|
||||
* @see \IPS\Patterns\ActiveRecord::save
|
||||
* @param mixed $key Key
|
||||
* @param mixed $value Value
|
||||
* @return void
|
||||
*/
|
||||
public function __set( $key, $value )
|
||||
{
|
||||
if( method_exists( $this, 'set_'.$key ) )
|
||||
{
|
||||
$oldValues = $this->_data;
|
||||
|
||||
call_user_func( array( $this, 'set_'.$key ), $value );
|
||||
|
||||
foreach( $this->_data as $k => $v )
|
||||
{
|
||||
if( !array_key_exists( $k, $oldValues ) or ( $v instanceof \IPS\Patterns\Bitwise and !( $oldValues[ $k ] instanceof \IPS\Patterns\Bitwise ) ) or $oldValues[ $k ] != $v )
|
||||
{
|
||||
$this->changed[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
unset( $oldValues );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_data[ $key ] = $value;
|
||||
$this->changed[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief By default cloning will create a new ActiveRecord record, but if you truly want an object copy you can set this to TRUE first and a direct copy will be returned
|
||||
*/
|
||||
public $skipCloneDuplication = FALSE;
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Duplicate
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if( $this->skipCloneDuplication === TRUE )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$primaryKey = static::$databaseColumnId;
|
||||
$this->$primaryKey = NULL;
|
||||
|
||||
$this->_new = TRUE;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Changed Columns
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ( $this->_new )
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
$data = $this->changed;
|
||||
}
|
||||
|
||||
foreach ( array_keys( static::$bitOptions ) as $k )
|
||||
{
|
||||
if ( $this->$k instanceof Bitwise )
|
||||
{
|
||||
foreach( $this->$k->values as $field => $value )
|
||||
{
|
||||
$data[ $field ] = intval( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->_new )
|
||||
{
|
||||
$insert = array();
|
||||
if( static::$databasePrefix === NULL )
|
||||
{
|
||||
$insert = $data;
|
||||
}
|
||||
else
|
||||
{
|
||||
$insert = array();
|
||||
foreach ( $data as $k => $v )
|
||||
{
|
||||
$insert[ static::$databasePrefix . $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$insertId = static::db()->insert( static::$databaseTable, $insert );
|
||||
|
||||
$primaryKey = static::$databaseColumnId;
|
||||
if ( $this->$primaryKey === NULL and $insertId )
|
||||
{
|
||||
$this->$primaryKey = $insertId;
|
||||
}
|
||||
|
||||
$this->_new = FALSE;
|
||||
|
||||
static::$multitons[ $this->$primaryKey ] = $this;
|
||||
}
|
||||
elseif( !empty( $data ) )
|
||||
{
|
||||
/* Set the column names with a prefix */
|
||||
if( static::$databasePrefix === NULL )
|
||||
{
|
||||
$update = $data;
|
||||
}
|
||||
else
|
||||
{
|
||||
$update = array();
|
||||
|
||||
foreach ( $data as $k => $v )
|
||||
{
|
||||
$update[ static::$databasePrefix . $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Work out the ID */
|
||||
$idColumn = static::$databaseColumnId;
|
||||
|
||||
/* Save */
|
||||
static::db()->update( static::$databaseTable, $update, array( static::$databasePrefix . $idColumn . '=?', $this->$idColumn ) );
|
||||
|
||||
/* Reset our log of what's changed */
|
||||
$this->changed = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Delete Record
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$idColumn = static::$databaseColumnId;
|
||||
static::db()->delete( static::$databaseTable, array( static::$databasePrefix . $idColumn . '=?', $this->$idColumn ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get follow data
|
||||
*
|
||||
* @param string $area Area
|
||||
* @param int $id ID
|
||||
* @param int $privacy static::FOLLOW_PUBLIC + static::FOLLOW_ANONYMOUS
|
||||
* @param array $frequencyTypes array( 'immediate', 'daily', 'weekly' )
|
||||
* @param \IPS\DateTime|int|NULL $date Only users who started following before this date will be returned. NULL for no restriction
|
||||
* @param int|array $limit LIMIT clause
|
||||
* @param string $order Column to order by
|
||||
* @param int $flags Flags to pass to select (e.g. \IPS\DB::SELECT_SQL_CALC_FOUND_ROWS)
|
||||
* @return \IPS\Db\Select
|
||||
* @throws |\BadMethodCallException
|
||||
*/
|
||||
protected static function _followers( $area, $id, $privacy, $frequencyTypes, $date, $limit, $order=NULL, $flags=\IPS\DB::SELECT_SQL_CALC_FOUND_ROWS )
|
||||
{
|
||||
/* Initial where clause */
|
||||
$where[] = array( 'follow_app=? AND follow_area=? AND follow_rel_id=?', static::$application, $area, $id );
|
||||
|
||||
/* Public / Anonymous */
|
||||
if ( !( $privacy & static::FOLLOW_PUBLIC ) )
|
||||
{
|
||||
$where[] = array( 'follow_is_anon=1' );
|
||||
}
|
||||
elseif ( !( $privacy & static::FOLLOW_ANONYMOUS ) )
|
||||
{
|
||||
$where[] = array( 'follow_is_anon=0' );
|
||||
}
|
||||
|
||||
/* Specific type */
|
||||
if ( $frequencyTypes != array( 'immediate', 'daily', 'weekly' ) )
|
||||
{
|
||||
$where[] = array( \IPS\Db::i()->in( 'follow_notify_freq', $frequencyTypes ) );
|
||||
}
|
||||
|
||||
/* Since */
|
||||
if( $date !== NULL )
|
||||
{
|
||||
$where[] = array( 'follow_added<?', ( $date instanceof \IPS\DateTime ) ? $date->getTimestamp() : intval( $date ) );
|
||||
}
|
||||
|
||||
/* Cache the results as this may be called multiple times in one page load */
|
||||
static $cache = array();
|
||||
$_hash = md5( json_encode( $where ) . $order . json_encode( $limit ) . $flags );
|
||||
|
||||
if( isset( $cache[ $_hash ] ) )
|
||||
{
|
||||
return $cache[ $_hash ];
|
||||
}
|
||||
|
||||
/* Get */
|
||||
if ( $order === 'name' )
|
||||
{
|
||||
$cache[ $_hash ] = \IPS\Db::i()->select( 'core_follow.*, core_members.name', 'core_follow', $where, 'name ASC', $limit, NULL, NULL, $flags )->join( 'core_members', array( 'core_members.member_id=core_follow.follow_member_id' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$cache[ $_hash ] = \IPS\Db::i()->select( 'core_follow.*', 'core_follow', $where, $order, $limit, NULL, NULL, $flags );
|
||||
}
|
||||
|
||||
return $cache[ $_hash ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cover Photo
|
||||
*
|
||||
* @return \IPS\Helpers\CoverPhoto
|
||||
*/
|
||||
public function coverPhoto()
|
||||
{
|
||||
$photoCol = static::$databaseColumnMap[ 'cover_photo' ];
|
||||
$photoOffset = static::$databaseColumnMap[ 'cover_photo_offset' ];
|
||||
$photo = new \IPS\Helpers\CoverPhoto;
|
||||
if ( isset( static::$databaseColumnMap['cover_photo'] ) and $this->$photoCol )
|
||||
{
|
||||
$photo->file = \IPS\File::get( static::$coverPhotoStorageExtension, $this->$photoCol );
|
||||
$photo->offset = $this->$photoOffset;
|
||||
}
|
||||
$photo->editable = $this->canEdit();
|
||||
$photo->object = $this;
|
||||
|
||||
return $photo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief ActiveRecord IteratorIterator
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @since 21 Nov 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Patterns;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ActiveRecord IteratorIterator
|
||||
*/
|
||||
class _ActiveRecordIterator extends \IteratorIterator implements \Countable
|
||||
{
|
||||
/**
|
||||
* @brief Classname
|
||||
*/
|
||||
public $classname;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Traversable $iterator The iterator
|
||||
* @param string $classname The classname
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( \Traversable $iterator, $classname )
|
||||
{
|
||||
$this->classname = $classname;
|
||||
return parent::__construct( $iterator );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current
|
||||
*
|
||||
* @return \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return call_user_func( array( $this->classname, 'constructFromData' ), parent::current() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count
|
||||
*
|
||||
* @param bool $allRows If TRUE, will get the number of rows ignoring the limit. In order for this to work, the query must have been ran with SQL_CALC_FOUND_ROWS
|
||||
* @return int
|
||||
*/
|
||||
public function count( $allRows = FALSE )
|
||||
{
|
||||
return (int) $this->getInnerIterator()->count( $allRows );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Bitwise Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @since 1 Apr 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Patterns;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise Class
|
||||
*/
|
||||
class _Bitwise implements \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @brief Values
|
||||
*/
|
||||
public $values = array();
|
||||
|
||||
/**
|
||||
* @brief Original Values
|
||||
*/
|
||||
public $originalValues = 0;
|
||||
|
||||
/**
|
||||
* @brief Keys
|
||||
*/
|
||||
protected $keys = array();
|
||||
|
||||
/**
|
||||
* @brief Keys Lookup
|
||||
*/
|
||||
protected $lookup = array();
|
||||
|
||||
/**
|
||||
* @brief Callback when value is changed
|
||||
*/
|
||||
protected $callback = NULL;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $values The numbers
|
||||
* @param array $keys Multi-dimensional array. Keys match keys in $value, value is associative array with keys and representive value, or just a list of keys in order
|
||||
* @param callback|null $callback Callback when value is changed
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( array $values, array $keys, $callback=NULL )
|
||||
{
|
||||
$this->values = $values;
|
||||
$this->originalValues = $values;
|
||||
|
||||
foreach ( $keys as $groupKey => $fields )
|
||||
{
|
||||
$i = 1;
|
||||
foreach ( $fields as $k => $v )
|
||||
{
|
||||
if ( is_string( $v ) )
|
||||
{
|
||||
$this->keys[ $groupKey ][ $v ] = $i;
|
||||
$this->lookup[ $k ] = $groupKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
while( $v != $i )
|
||||
{
|
||||
$this->keys[ $groupKey ][] = $i;
|
||||
$this->lookup[] = $groupKey;
|
||||
$i *= 2;
|
||||
}
|
||||
|
||||
$this->keys[ $groupKey ][ $k ] = $v;
|
||||
$this->lookup[ $k ] = $groupKey;
|
||||
}
|
||||
|
||||
$i *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset exists?
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this->lookup[ $offset ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offset
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
$group = $this->lookup[ $offset ];
|
||||
return (bool) ( $this->values[ $group ] & $this->keys[ $group ][ $offset ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set offset
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @param bool $value Value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
if ( $this->callback !== NULL )
|
||||
{
|
||||
call_user_func( $this->callback, $offset, $value );
|
||||
}
|
||||
|
||||
$group = $this->lookup[ $offset ];
|
||||
if ( $value )
|
||||
{
|
||||
$this->values[ $group ] |= $this->keys[ $group ][ $offset ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->values[ $group ] &= ~$this->keys[ $group ][ $offset ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset offset
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
return $this->offsetSet( $offset, FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function asArray()
|
||||
{
|
||||
$return = array();
|
||||
|
||||
foreach ( $this->keys as $group )
|
||||
{
|
||||
foreach ( $group as $k => $v )
|
||||
{
|
||||
$return[ $k ] = $this->offsetGet( $k );
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Singleton Pattern
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @since 18 Feb 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Patterns;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton Pattern
|
||||
*/
|
||||
class _Singleton implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @brief Singleton Instances
|
||||
* @note This needs to be declared in any child classes as well, only declaring here for editor code-complete/error-check functionality
|
||||
*/
|
||||
protected static $instance = NULL;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function i()
|
||||
{
|
||||
if( static::$instance === NULL )
|
||||
{
|
||||
$classname = get_called_class();
|
||||
static::$instance = new $classname;
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Data Store
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
* Magic Method: Get
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @return mixed Value from the datastore
|
||||
*/
|
||||
public function __get( $key )
|
||||
{
|
||||
if( !isset( $this->data[ $key ] ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $this->data[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method: Set
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @param mixed $value Value
|
||||
* @return void
|
||||
*/
|
||||
public function __set( $key, $value )
|
||||
{
|
||||
$this->data[ $key ] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method: Isset
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset( $key )
|
||||
{
|
||||
return isset( $this->data[ $key ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator: Rewind
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function rewind()
|
||||
{
|
||||
reset( $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator: Current
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function current()
|
||||
{
|
||||
return current( $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator: Key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function key()
|
||||
{
|
||||
return key( $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator: Next
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function next()
|
||||
{
|
||||
next( $this->data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator: Valid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function valid()
|
||||
{
|
||||
return key( $this->data ) !== null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Union Iterator Pattern
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @since 14 Nov 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Patterns;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union Iterator Pattern
|
||||
*/
|
||||
class _UnionIterator implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @brief Iterators
|
||||
*/
|
||||
protected $iterators;
|
||||
|
||||
/**
|
||||
* @brief Current lowest value
|
||||
*/
|
||||
protected $currentIteratorKey;
|
||||
|
||||
/**
|
||||
* @brief Order Direction
|
||||
*/
|
||||
protected $orderDirection;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $orderDirection "asc" or "desc"
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $orderDirection )
|
||||
{
|
||||
$this->iterators = new \SplObjectStorage;
|
||||
$this->orderDirection = $orderDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach Iterator
|
||||
*
|
||||
* @param \Traverable $iterator Iterator
|
||||
* @param mixed $key The name of the property which contains the value used to compare positions
|
||||
* @return void
|
||||
*/
|
||||
public function attachIterator( \Traversable $iterator, $key )
|
||||
{
|
||||
$this->iterators->attach( $iterator, $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
foreach ( $this->iterators as $iterator )
|
||||
{
|
||||
$iterator->rewind();
|
||||
}
|
||||
$this->currentLowestValue = NULL;
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current row
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
foreach ( $this->iterators as $k => $iterator )
|
||||
{
|
||||
if ( $k === $this->currentIteratorKey )
|
||||
{
|
||||
$return = $iterator->current();
|
||||
$iterator->next();
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch next result
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->currentIteratorKey = NULL;
|
||||
$currentLowestValue = NULL;
|
||||
|
||||
$values = array();
|
||||
foreach ( $this->iterators as $k => $iterator )
|
||||
{
|
||||
if ( $iterator->valid() )
|
||||
{
|
||||
$col = $this->iterators->getInfo();
|
||||
$current = $iterator->current();
|
||||
if ( is_array( $current ) )
|
||||
{
|
||||
$value = $current[ $col ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $current->$col;
|
||||
}
|
||||
|
||||
if ( $value instanceof \IPS\DateTime )
|
||||
{
|
||||
$value = $value->getTimestamp();
|
||||
}
|
||||
|
||||
if ( $currentLowestValue === NULL or ( $this->orderDirection === 'asc' and $value < $currentLowestValue ) or ( $this->orderDirection === 'desc' and $value > $currentLowestValue ) )
|
||||
{
|
||||
$currentLowestValue = $value;
|
||||
$this->currentIteratorKey = $k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current row valid?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->currentIteratorKey !== NULL;
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
Reference in new issue
Block a user