Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+44 -18
View File
@@ -11,7 +11,7 @@
namespace IPS;
/* 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;
@@ -50,7 +50,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
/* This is here for legacy purposes - background queue tasks should throw \IPS\Task\Queue\OutOfRangeException to indicate they are completed, but
for now we'll still support this and log it for investigation */
if ( is_null( $newOffset ) )
if ( \is_null( $newOffset ) )
{
\IPS\Log::log( $queueData['key'] . " returned a NULL offset - tasks should throw \\IPS\\Task\\Queue\\OutOfRangeException when they are finished", 'runQueue_log' );
\IPS\Db::i()->delete( 'core_queue', array( 'id=?', $queueData['id'] ) );
@@ -58,7 +58,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
/* Do we have a post-completion callback? */
if( method_exists( $class, 'postComplete' ) )
{
$class->postComplete( $queueData );
$class->postComplete( $queueData, TRUE );
}
}
/* Task completed successfully and a new offset was returned - store the value and then return it */
@@ -88,9 +88,16 @@ class _Task extends \IPS\Patterns\ActiveRecord
/* Do we have a post-completion callback? */
if( isset( $class ) AND method_exists( $class, 'postComplete' ) )
{
$class->postComplete( $queueData );
$class->postComplete( $queueData, TRUE );
}
}
/* Catch any OORE in the task and transform the exception so that we can display an error */
catch( \OutOfRangeException $e )
{
\IPS\Log::log( $e, 'queue_oore');
throw new \RuntimeException( $e->getMessage() );
}
return $queueData;
}
@@ -127,11 +134,16 @@ class _Task extends \IPS\Patterns\ActiveRecord
$data = $class->preQueueData( $data );
if ( $data === NULL )
{
if ( method_exists( $class, 'postComplete' ) )
{
$class->postComplete( $data, FALSE );
}
return;
}
}
if ( is_array( $checkForDuplicationKeys ) and is_array( $data ) )
if ( \is_array( $checkForDuplicationKeys ) and \is_array( $data ) )
{
$insert = FALSE;
foreach( \IPS\Db::i()->select( '*', 'core_queue', array( '`app`=? AND `key`=?', $app, $key ) ) as $row )
@@ -149,7 +161,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
}
}
if ( $got === count( $checkForDuplicationKeys ) )
if ( $got === \count( $checkForDuplicationKeys ) )
{
/* Ok, so we have a duplicate queue item, lets remove it so the new one which is set with the correct count is used and offset is returned to 0 to start over */
\IPS\Db::i()->delete( 'core_queue', array( 'id=?', $row['id'] ) );
@@ -240,10 +252,16 @@ class _Task extends \IPS\Patterns\ActiveRecord
* @throws \IPS\Task\Exception
*/
public function run()
{
{
/* Enforce Lock. If affected rows = 0, the task has been locked by another process */
if( (int) \IPS\Db::i()->update( 'core_tasks', array( 'next_run' => time(), 'running' => 1 ), array( '`running`=0 AND `id`=?', $this->id ) ) === 0 )
{
return NULL;
}
/* Keep the task object aligned with the database changes made above */
$this->running = TRUE;
$this->next_run = time();
$this->save();
$output = $this->execute();
@@ -253,6 +271,8 @@ class _Task extends \IPS\Patterns\ActiveRecord
$this->last_run = \IPS\DateTime::create()->getTimestamp();
$this->save();
\IPS\core\AdminNotification::remove( 'core', 'ConfigurationError', "taskLock-{$this->id}" );
return $output;
}
@@ -282,7 +302,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
public function runUntilTimeout( $callback, $limit=NULL )
{
/* Work out the maximum execution time */
$timeLeft = 45;
$timeLeft = 90;
if ( $phpMaxExecutionTime = ini_get('max_execution_time') and $phpMaxExecutionTime <= $timeLeft )
{
$timeLeft = $phpMaxExecutionTime - 2;
@@ -306,7 +326,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
/* Work out the memory limit */
$memoryLeft = 0;
$memoryUnlimited = FALSE;
if ( function_exists( 'memory_get_usage' ) )
if ( \function_exists( 'memory_get_usage' ) )
{
$memory_limit = ini_get('memory_limit');
if ( $memory_limit == -1 )
@@ -341,10 +361,10 @@ class _Task extends \IPS\Patterns\ActiveRecord
{
/* Start a timer */
$timer = microtime( TRUE );
$memoryTimer = function_exists( 'memory_get_usage' ) ? memory_get_usage( TRUE ) : 0;
$memoryTimer = \function_exists( 'memory_get_usage' ) ? memory_get_usage( TRUE ) : 0;
/* Execute */
if ( call_user_func( $callback ) === FALSE )
if ( $callback() === FALSE )
{
break;
}
@@ -360,7 +380,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
/* Decrease the time left */
$timeTheLastRunTook = round( ( microtime( TRUE ) - $timer ), 2 );
$timeLeft -= $timeTheLastRunTook;
$memoryTheLastRunTook = function_exists( 'memory_get_usage' ) ? ( memory_get_usage( TRUE ) - $memoryTimer ) : 0;
$memoryTheLastRunTook = \function_exists( 'memory_get_usage' ) ? ( memory_get_usage( TRUE ) - $memoryTimer ) : 0;
if ( !$memoryUnlimited )
{
$memoryLeft = $memory_limit - memory_get_usage( TRUE );
@@ -383,6 +403,11 @@ class _Task extends \IPS\Patterns\ActiveRecord
if ( $this->lock_count < 3 ) // Allowing this to grow infinitely will eventually overflow. The warning triggers at 3, so we only need to know if it's more than 3 or not.
{
$this->lock_count++;
if ( $this->lock_count >= 3 )
{
\IPS\core\AdminNotification::send( 'core', 'ConfigurationError', "taskLock-{$this->id}" );
}
}
$this->next_run = \IPS\DateTime::create()->add( new \DateInterval( $this->frequency ) )->getTimestamp();
$this->save();
@@ -544,8 +569,8 @@ class _Task extends \IPS\Patterns\ActiveRecord
/* Add to DB */
$frequency = "P{$values['dev_task_frequency']->y}Y{$values['dev_task_frequency']->m}M{$values['dev_task_frequency']->d}DT{$values['dev_task_frequency']->h}H{$values['dev_task_frequency']->i}M{$values['dev_task_frequency']->s}S";
\IPS\Db::i()->replace( 'core_tasks', array(
'app' => is_string( $appKeyOrPluginId ) ? $appKeyOrPluginId : NULL,
'plugin' => is_numeric( $appKeyOrPluginId ) ? $appKeyOrPluginId : NULL,
'app' => \is_string( $appKeyOrPluginId ) ? $appKeyOrPluginId : NULL,
'plugin' => \is_numeric( $appKeyOrPluginId ) ? $appKeyOrPluginId : NULL,
'key' => $values['dev_task_key'],
'frequency' => $frequency,
'next_run' => \IPS\DateTime::create()->add( new \DateInterval( $frequency ) )->getTimestamp(),
@@ -569,7 +594,8 @@ class _Task extends \IPS\Patterns\ActiveRecord
return $form;
case 'delete':
\IPS\Session::i()->csrfCheck();
$tasks = json_decode( file_get_contents( $json ), TRUE );
if ( array_key_exists( \IPS\Request::i()->key, $tasks ) )
{
@@ -581,7 +607,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
unlink( $taskDirectory . "/" . \IPS\Request::i()->key . ".php" );
}
\IPS\Db::i()->delete( 'core_tasks', array( ( is_string( $appKeyOrPluginId ) ? 'app' : 'plugin' ) . '=? AND `key`=?', $appKeyOrPluginId, \IPS\Request::i()->key ) );
\IPS\Db::i()->delete( 'core_tasks', array( ( \is_string( $appKeyOrPluginId ) ? 'app' : 'plugin' ) . '=? AND `key`=?', $appKeyOrPluginId, \IPS\Request::i()->key ) );
}
\IPS\Output::i()->redirect( $url, 'saved' );
@@ -617,7 +643,7 @@ class _Task extends \IPS\Patterns\ActiveRecord
'delete' => array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => $url->setQueryString( 'taskTable', 'delete' )->setQueryString( 'key', $row['dev_task_key'] ),
'link' => $url->setQueryString( 'taskTable', 'delete' )->setQueryString( 'key', $row['dev_task_key'] )->csrf(),
'data' => array( 'delete' => '' )
)
);