Created
December 10, 2019 06:29
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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] | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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', | |
]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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', | |
]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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