Skip to content

Instantly share code, notes, and snippets.

@stevebauman
Created December 10, 2019 06:29
Show Gist options
  • Save stevebauman/ddfb3b83e8981beec3a1ce7efdfd789f to your computer and use it in GitHub Desktop.
Save stevebauman/ddfb3b83e8981beec3a1ce7efdfd789f to your computer and use it in GitHub Desktop.
A Scheduled Tasks XML File Generator using PHP, Laravel and Spatie - Array-to-XML
<?php
namespace App\System;
use Spatie\ArrayToXml\ArrayToXml;
trait GeneratesXml
{
/**
* The XML template.
*
* @return array
*/
abstract public function template();
/**
* The root XML attributes.
*
* @return array
*/
public function rootAttributes()
{
return [];
}
/**
* Generates XML based on the template.
*
* @return string
*/
public function toXml()
{
return ArrayToXml::convert(
$this->template(),
$this->rootAttributes(),
$replaceSpacesByUnderScoresInKeyNames = true,
$xmlEncoding = 'UTF-16',
$xmlVersion = '1.0',
['formatOutput' => true]
);
}
}
<?php
namespace App\System;
class QueueTask extends ScheduledTask
{
/**
* The task attributes.
*
* @var array
*/
protected $attributes = [
'name' => 'ScoutQueueRunner',
'author' => 'Scout',
'description' => 'Processes the Scout job queue.',
'user_id' => ScheduledTask::USER_SYSTEM,
'interval' => 'PT5M',
'time_limit' => 'PT72H',
// We will use the queue:listen command in case updates are
// performed on the application. This is to prevent having
// to restart the task manually on the server.
'command' => 'queue:listen --tries=3',
];
}
<?php
namespace App\System;
use Illuminate\Support\Str;
use Illuminate\Support\Fluent;
use Illuminate\Support\Facades\File;
use Symfony\Component\Process\PhpExecutableFinder;
class ScheduledTask extends Fluent
{
use GeneratesXml;
const USER_SYSTEM = 'S-1-5-18';
/**
* The format to use for the scheduled task dates.
*
* @var string
*/
protected $dateFormat = 'Y-m-d\TH:i:s';
/**
* Create the scheduled task XML file.
*
* @return string
*/
public function create()
{
$path = $this->path();
File::put($path, $this->toXml());
return $path;
}
/**
* Determine if the scheduled task file exists.
*
* @return bool
*/
public function exists()
{
return File::exists($this->path());
}
/**
* Get the full file path of the XML document.
*
* @return string
*/
public function path()
{
return storage_path(sprintf('app'.DIRECTORY_SEPARATOR.'%s.xml', $this->name));
}
/**
* Generate a command for importing the scheduled task.
*
* @return string
*/
public function command()
{
return sprintf('schtasks /Create /TN "%s" /XML "%s" /F', $this->name, $this->path());
}
/**
* Get the triggers for the scheduled task.
*
* @return TaskTrigger[]
*/
public function triggers()
{
return [
// We will enable a registration trigger to trigger the task
// as soon as it's imported. Then, the boot trigger will
// take over if the server is ever restarted.
TaskTrigger::registration([
'Repetition' => [
'Interval' => $this->interval,
'StopAtDurationEnd' => 'false',
],
'StartBoundary' => $this->getStartDate(),
]),
TaskTrigger::boot([
'Repetition' => [
'Interval' => $this->interval,
'StopAtDurationEnd' => 'false',
],
'StartBoundary' => $this->getStartDate(),
]),
// We will create a daily calendar trigger to regularly try starting
// the task in case it fails. This trigger should begin once the
// task is imported for the first time.
TaskTrigger::calendar([
'Repetition' => [
'Interval' => $this->interval,
'StopAtDurationEnd' => 'false',
],
'StartBoundary' => $this->getStartDate(),
'ScheduleByDay' => [
'DaysInterval' => 1
],
]),
];
}
/**
* Get the scheduled task triggers mapped via key.
*
* @return array
*/
protected function getMappedTriggers()
{
$triggers = [];
foreach ($this->triggers() as $trigger) {
$triggers[$trigger->getRootElementName()] = $trigger->toArray();
}
return $triggers;
}
/**
* Get the start date of the task.
*
* @return string
*/
protected function getStartDate()
{
return $this->get('start', now()->subMinute()->format($this->dateFormat));
}
/**
* The XML template.
*
* @return array
*/
protected function template()
{
return [
'RegistrationInfo' => [
'Date' => $this->get('date', now()->format($this->dateFormat)),
'Author' => $this->author,
'Description' => $this->description,
'URI' => Str::start($this->name, '\\'),
],
'Triggers' => $this->getMappedTriggers(),
'Principals' => [
'Principal' => [
'_attributes' => [
'id' => 'Author',
],
'UserId' => $this->user_id,
'RunLevel' => 'LeastPrivilege',
],
],
'Settings' => [
'MultipleInstancesPolicy' => 'IgnoreNew',
'DisallowStartIfOnBatteries' => 'true',
'StopIfGoingOnBatteries' => 'true',
'AllowHardTerminate' => 'true',
'StartWhenAvailable' => 'true',
'RunOnlyIfNetworkAvailable' => 'false',
'IdleSettings' => [
'StopOnIdleEnd' => 'true',
'RestartOnIdle' => 'true',
],
'AllowStartOnDemand' => 'true',
'Enabled' => 'true',
'Hidden' => 'false',
'RunOnlyIfIdle' => 'false',
'WakeToRun' => 'false',
'ExecutionTimeLimit' => $this->time_limit,
'Priority' => 7,
],
'Actions' => [
'_attributes' => [
'Context' => 'Author',
],
'Exec' => [
'Command' => $this->phpExecutable(),
'Arguments' => sprintf('artisan %s', $this->command),
'WorkingDirectory' => $this->get('path', base_path()),
],
]
];
}
/**
* The root XML document properties.
*
* @return array
*/
protected function rootAttributes()
{
return [
'rootElementName' => 'Task',
'_attributes' => [
'xmlns' => 'http://schemas.microsoft.com/windows/2004/02/mit/task',
'version' => '1.2',
],
];
}
/**
* Get the PHP executable path.
*
* @return string
*/
protected function phpExecutable()
{
return (new PhpExecutableFinder())->find() ?? 'php';
}
}
<?php
namespace App\System;
class SchedulerTask extends ScheduledTask
{
/**
* The task attributes.
*
* @var array
*/
protected $attributes = [
'name' => 'ScoutScheduleRunner',
'author' => 'Scout',
'description' => 'Processes the Scout scheduled commands.',
'user_id' => ScheduledTask::USER_SYSTEM,
'interval' => 'PT1M',
'time_limit' => 'PT30M',
'command' => 'schedule:run',
];
}
<?php
namespace App\System;
use Illuminate\Support\Fluent;
class TaskTrigger extends Fluent
{
/**
* The default task attributes.
*
* @var array
*/
protected $attributes = [
'Enabled' => 'true',
];
/**
* Get the root element name of the trigger.
*
* @var string|null
*/
protected $rootElementName;
/**
* Generate a calendar trigger.
*
* @param array $attributes
*
* @return static
*/
public static function calendar(array $attributes = [])
{
return (new static($attributes))->setRootElementName('CalendarTrigger');
}
/**
* Generate a boot trigger.
*
* @param array $attributes
*
* @return static
*/
public static function boot(array $attributes = [])
{
return (new static($attributes))->setRootElementName('BootTrigger');
}
/**
* Generate a logon trigger.
*
* @param array $attributes
*
* @return static
*/
public static function logon(array $attributes = [])
{
return (new static($attributes))->setRootElementName('LogonTrigger');
}
/**
* Generate an idle trigger.
*
* @param array $attributes
*
* @return static
*/
public static function idle(array $attributes = [])
{
return (new static($attributes))->setRootElementName('IdleTrigger');
}
/**
* Generate an event trigger.
*
* @param array $attributes
*
* @return static
*/
public static function event(array $attributes = [])
{
return (new static($attributes))->setRootElementName('EventTrigger');
}
/**
* Generate a registration trigger.
*
* @param array $attributes
*
* @return static
*/
public static function registration(array $attributes = [])
{
return (new static($attributes))->setRootElementName('RegistrationTrigger');
}
/**
* Generate a state change trigger.
*
* @param array $attributes
*
* @return static
*/
public static function sessionStateChange(array $attributes = [])
{
return (new static($attributes))->setRootElementName('SessionStateChangeTrigger');
}
/**
* Set the root task element name.
*
* @param string $name
*
* @return $this
*/
public function setRootElementName($name)
{
$this->rootElementName = $name;
return $this;
}
/**
* Get the task root element name.
*
* @return string|null
*/
public function getRootElementName()
{
return $this->rootElementName;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment