275 lines
6.8 KiB
PHP
275 lines
6.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Bnomei;
|
|
|
|
use Kirby\Cms\File;
|
|
use Kirby\Cms\Page;
|
|
use Kirby\Cms\User;
|
|
use Kirby\Toolkit\A;
|
|
use Kirby\Toolkit\Str;
|
|
use League\CLImate\CLImate;
|
|
|
|
final class Janitor
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $options;
|
|
|
|
/**
|
|
* Janitor constructor.
|
|
* @param array $options
|
|
*/
|
|
public function __construct(array $options = [])
|
|
{
|
|
$defaults = [
|
|
'debug' => option('debug'),
|
|
'log' => option('bnomei.janitor.log.fn'),
|
|
'jobs' => option('bnomei.janitor.jobs'),
|
|
'jobs-defaults' => ['bnomei.janitor.jobs-defaults'],
|
|
'jobs-extends' => option('bnomei.janitor.jobs-extends'),
|
|
'secret' => option('bnomei.janitor.secret'),
|
|
];
|
|
$this->options = array_merge($defaults, $options);
|
|
|
|
$extends = array_merge($this->options['jobs-defaults'], $this->options['jobs-extends']);
|
|
foreach ($extends as $extend) {
|
|
// NOTE: it is intended that jobs override merged not other way around
|
|
$this->options['jobs'] = array_change_key_case(
|
|
array_merge(option($extend, []), $this->options['jobs'])
|
|
);
|
|
}
|
|
|
|
foreach ($this->options as $key => $call) {
|
|
if (is_callable($call) && in_array($key, ['secret'])) {
|
|
$this->options[$key] = $call();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|null $key
|
|
* @return array
|
|
*/
|
|
public function option(?string $key = null)
|
|
{
|
|
if ($key) {
|
|
return A::get($this->options, $key);
|
|
}
|
|
return $this->options;
|
|
}
|
|
|
|
/**
|
|
* @param string $secret
|
|
* @param string $name
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public function jobWithSecret(string $secret, string $name, array $data = []): array
|
|
{
|
|
if ($secret === $this->option('secret')) {
|
|
return $this->job($name, $data);
|
|
}
|
|
return [
|
|
'status' => 401,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public function job(string $name, array $data = []): array
|
|
{
|
|
$job = $this->findJob($name);
|
|
|
|
if (!is_string($job) && is_callable($job)) {
|
|
return $this->jobFromCallable($job, $data);
|
|
} elseif (class_exists($job)) {
|
|
return $this->jobFromClass($job, $data);
|
|
}
|
|
|
|
return [
|
|
'status' => 404,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
public function listJobs()
|
|
{
|
|
// find in jobs config
|
|
return array_keys($this->option('jobs'));
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @return mixed|string
|
|
*/
|
|
public function findJob(string $name)
|
|
{
|
|
// find in jobs config
|
|
$jobInConfig = A::get($this->option('jobs'), strtolower($name));
|
|
if ($jobInConfig) {
|
|
return $jobInConfig;
|
|
}
|
|
|
|
// could be a class
|
|
return $name;
|
|
}
|
|
|
|
/**
|
|
* @param $job
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public function jobFromCallable($job, array $data): array
|
|
{
|
|
$return = false;
|
|
try {
|
|
set_time_limit(0);
|
|
} catch (\Exception $ex) {
|
|
// ignore
|
|
}
|
|
|
|
try {
|
|
$return = $job(
|
|
page(str_replace('+', '/', urldecode(A::get($data, 'contextPage', '')))),
|
|
str_replace('+S_L_A_S_H+', '/', urldecode(A::get($data, 'contextData', '')))
|
|
);
|
|
} catch (\BadMethodCallException $ex) {
|
|
$return = $job();
|
|
}
|
|
if (is_array($return)) {
|
|
return $return;
|
|
}
|
|
return [
|
|
'status' => $return ? 200 : 404,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param string $job
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
public function jobFromClass(string $job, array $data): array
|
|
{
|
|
$object = new $job(
|
|
page(str_replace('+', '/', urldecode(A::get($data, 'contextPage', '')))),
|
|
str_replace('+S_L_A_S_H+', '/', urldecode(A::get($data, 'contextData', '')))
|
|
);
|
|
|
|
if (method_exists($object, 'job')) {
|
|
try {
|
|
set_time_limit(0);
|
|
} catch (\Exception $ex) {
|
|
// ignore
|
|
}
|
|
return $object->job();
|
|
}
|
|
return [
|
|
'status' => 400,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param string $msg
|
|
* @param string $level
|
|
* @param array $context
|
|
* @return bool
|
|
*/
|
|
public function log(string $msg = '', string $level = 'info', array $context = []): bool
|
|
{
|
|
$log = $this->option('log');
|
|
if ($log && is_callable($log)) {
|
|
if (!$this->option('debug') && $level == 'debug') {
|
|
// skip but...
|
|
return true;
|
|
} else {
|
|
return $log($msg, $level, $context);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* @var Janitor
|
|
*/
|
|
private static $singleton;
|
|
|
|
/**
|
|
* @param array $options
|
|
* @return Janitor
|
|
*/
|
|
public static function singleton(array $options = []): Janitor
|
|
{
|
|
if (self::$singleton) {
|
|
return self::$singleton;
|
|
}
|
|
|
|
self::$singleton = new Janitor($options);
|
|
return self::$singleton;
|
|
}
|
|
|
|
/**
|
|
* @param string|null $template
|
|
* @param mixed|null $model
|
|
* @return string
|
|
*/
|
|
public static function query(string $template = null, $model = null): string
|
|
{
|
|
$page = null;
|
|
$file = null;
|
|
$user = kirby()->user();
|
|
if ($model && $model instanceof Page) {
|
|
$page = $model;
|
|
} elseif ($model && $model instanceof File) {
|
|
$file = $model;
|
|
} elseif ($model && $model instanceof User) {
|
|
$user = $model;
|
|
}
|
|
return Str::template($template, [
|
|
'kirby' => kirby(),
|
|
'site' => kirby()->site(),
|
|
'page' => $page,
|
|
'file' => $file,
|
|
'user' => $user,
|
|
'model' => $model ? get_class($model) : null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param $val
|
|
* @param bool $return_null
|
|
* @return bool
|
|
*/
|
|
public static function isTrue($val, $return_null = false): bool
|
|
{
|
|
$boolval = (is_string($val) ? filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : (bool) $val);
|
|
$boolval = ($boolval === null && !$return_null ? false : $boolval);
|
|
return $boolval;
|
|
}
|
|
|
|
/**
|
|
* @var \League\CLImate\CLImate
|
|
*/
|
|
private static $climate;
|
|
|
|
/**
|
|
* @param CLImate|null $climate
|
|
* @return CLImate|null
|
|
*/
|
|
public static function climate(?CLImate $climate = null): ?CLImate
|
|
{
|
|
if ($climate && !self::$climate) {
|
|
self::$climate = $climate;
|
|
}
|
|
return self::$climate;
|
|
}
|
|
}
|