modifs.mineures sans commentaires
This commit is contained in:
parent
a7ac59ec9c
commit
efdf473097
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use nur\cli\Application;
|
||||
use nur\sery\wip\app\app;
|
||||
use nur\sery\wip\app\launcher;
|
||||
use nur\yaml;
|
||||
|
||||
Application::run(new class extends Application {
|
||||
const ACTION_INFOS = 0, ACTION_START = 1, ACTION_STOP = 2;
|
||||
|
||||
const ARGS = [
|
||||
"merge" => parent::ARGS,
|
||||
"purpose" => "lancer une tâche de fond",
|
||||
"usage" => "ApplicationClass args...",
|
||||
|
||||
["-s", "--start", "name" => "action", "value" => self::ACTION_START,
|
||||
"help" => "démarrer la tâche, c'est la valeur par défaut"
|
||||
],
|
||||
["-k", "--stop", "name" => "action", "value" => self::ACTION_STOP,
|
||||
"help" => "arrêter la tâche"
|
||||
],
|
||||
["-i", "--infos", "name" => "action", "value" => self::ACTION_INFOS,
|
||||
"help" => "afficher des informations sur la tâche"
|
||||
],
|
||||
];
|
||||
|
||||
protected $action = self::ACTION_START;
|
||||
|
||||
protected $args;
|
||||
|
||||
function main() {
|
||||
$appClass = $this->args[0] ?? null;
|
||||
if ($appClass === null) {
|
||||
self::die("Vous devez spécifier la classe de l'application");
|
||||
}
|
||||
$args = array_slice($this->args, 1);
|
||||
|
||||
$useRunfile = constant("$appClass::USE_RUNFILE");
|
||||
if (!$useRunfile) {
|
||||
self::die("Cette application ne supporte pas l'usage de runfile");
|
||||
}
|
||||
|
||||
$app = app::with($appClass);
|
||||
$runfile = $app->getRunfile();
|
||||
switch ($this->action) {
|
||||
case self::ACTION_START:
|
||||
launcher::_start($args, $runfile);
|
||||
break;
|
||||
case self::ACTION_STOP:
|
||||
launcher::_stop($runfile);
|
||||
break;
|
||||
case self::ACTION_INFOS:
|
||||
yaml::dump($runfile->read());
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -9,7 +9,7 @@ use nur\config\ArrayConfig;
|
|||
use nur\msg;
|
||||
use nur\os;
|
||||
use nur\path;
|
||||
use nur\sery\app\app;
|
||||
use nur\sery\wip\app\app;
|
||||
use nur\sery\output\log as nlog;
|
||||
use nur\sery\output\msg as nmsg;
|
||||
use nur\sery\output\console as nconsole;
|
||||
|
|
|
@ -40,6 +40,7 @@ class RunFile {
|
|||
return [
|
||||
"name" => $this->name,
|
||||
"id" => bin2hex(random_bytes(16)),
|
||||
"pg_pid" => null,
|
||||
"pid" => posix_getpid(),
|
||||
"serial" => 0,
|
||||
"date_start" => $dateStart,
|
||||
|
@ -127,45 +128,65 @@ class RunFile {
|
|||
return [$file, $data];
|
||||
}
|
||||
|
||||
protected function serialize(SharedFile $file, array $data, ?array $merge=null): void {
|
||||
$file->serialize(self::merge($data, $merge), true, true);
|
||||
}
|
||||
|
||||
protected function update(callable $func): void {
|
||||
[$file, $data] = $this->willWrite();
|
||||
$merge = call_user_func($func, $data);
|
||||
$this->serialize($file, $data, $merge);
|
||||
}
|
||||
|
||||
/** indiquer que l'application démarre */
|
||||
function start(): void {
|
||||
$this->file->serialize($this->initData());
|
||||
$this->update(function (array $data) {
|
||||
# garder l'identifiant de process
|
||||
$pgPid = $data["pg_pid"] ?? null;
|
||||
return cl::merge($this->initData(), [
|
||||
"pg_pid" => $pgPid,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/** indiquer le début d'une action */
|
||||
function action(?string $title, ?int $maxSteps=null): void {
|
||||
[$file, $data] = $this->willWrite();
|
||||
$file->serialize(self::merge($data, [
|
||||
"action" => $title,
|
||||
"action_date_start" => new DateTime(),
|
||||
"action_max_step" => $maxSteps,
|
||||
"action_current_step" => 0,
|
||||
]));
|
||||
$this->update(function (array $data) use ($title, $maxSteps) {
|
||||
return [
|
||||
"action" => $title,
|
||||
"action_date_start" => new DateTime(),
|
||||
"action_max_step" => $maxSteps,
|
||||
"action_current_step" => 0,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/** indiquer qu'une étape est franchie dans l'action en cours */
|
||||
function step(int $nbSteps=1): void {
|
||||
[$file, $data] = $this->willWrite();
|
||||
$file->serialize(self::merge($data, [
|
||||
"action_date_step" => new DateTime(),
|
||||
"action_current_step" => $data["action_current_step"] + $nbSteps,
|
||||
]));
|
||||
$this->update(function (array $data) use ($nbSteps) {
|
||||
return [
|
||||
"action_date_step" => new DateTime(),
|
||||
"action_current_step" => $data["action_current_step"] + $nbSteps,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/** indiquer que l'application s'arrête */
|
||||
function stop(): void {
|
||||
[$file, $data] = $this->willWrite();
|
||||
$file->serialize(self::merge($data, [
|
||||
"date_stop" => new DateTime(),
|
||||
]));
|
||||
$this->update(function (array $data) {
|
||||
return ["date_stop" => new DateTime()];
|
||||
});
|
||||
}
|
||||
|
||||
/** après l'arrêt de l'application, mettre à jour le code de retour */
|
||||
function stopped(int $exitcode): void {
|
||||
[$file, $data] = $this->willWrite();
|
||||
$file->serialize(self::merge($data, [
|
||||
"exitcode" => $exitcode,
|
||||
]));
|
||||
$this->update(function (array $data) use ($exitcode) {
|
||||
return [
|
||||
"pg_pid" => null,
|
||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
||||
"exitcode" => $exitcode,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
function getLockFile(?string $name=null, ?string $title=null): LockFile {
|
||||
|
@ -175,4 +196,61 @@ class RunFile {
|
|||
$name = str::join("/", [$this->name, $name]);
|
||||
return new LockFile($file, $name, $title);
|
||||
}
|
||||
|
||||
/** démarrer un groupe de process */
|
||||
function tm_startPg(): void {
|
||||
$this->update(function (array $data) {
|
||||
posix_setsid();
|
||||
return [
|
||||
"pg_pid" => posix_getpid(),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* vérifier si on est dans le cas où la tâche devrait tourner mais en réalité
|
||||
* ce n'est pas le cas
|
||||
*/
|
||||
function tm_isUndead(?int $pid=null): bool {
|
||||
$data = $this->read();
|
||||
if ($data["date_start"] === null) return false;
|
||||
if ($data["date_stop"] !== null) return false;
|
||||
$pid ??= $data["pid"];
|
||||
if (!posix_kill($pid, 0)) {
|
||||
switch (posix_get_last_error()) {
|
||||
case PCNTL_ESRCH:
|
||||
# process inexistant
|
||||
return true;
|
||||
case PCNTL_EPERM:
|
||||
# process auquel on n'a pas accès?! est-ce un autre process qui a
|
||||
# réutilisé le PID?
|
||||
return false;
|
||||
case PCNTL_EINVAL:
|
||||
# ne devrait pas se produire
|
||||
return false;
|
||||
}
|
||||
}
|
||||
# process existant auquel on a accès
|
||||
return false;
|
||||
}
|
||||
|
||||
function tm_isReapable(): bool {
|
||||
$data = $this->read();
|
||||
return $data["date_stop"] !== null && $data["exitcode"] === null;
|
||||
}
|
||||
|
||||
/** marquer la tâche comme terminée */
|
||||
function tm_reap(?int $pid=null): void {
|
||||
$data = $this->read();
|
||||
$pid ??= $data["pid"];
|
||||
pcntl_waitpid($pid, $status);
|
||||
$exitcode = pcntl_wifexited($status)? pcntl_wexitstatus($status): 127;
|
||||
$this->update(function (array $data) use ($exitcode) {
|
||||
return [
|
||||
"pg_pid" => null,
|
||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
||||
"exitcode" => $data["exitcode"] ?? $exitcode,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
16
src/str.php
16
src/str.php
|
@ -242,6 +242,22 @@ class str {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ajouter $sep$prefix$text$suffix à $s si $text est non vide
|
||||
*
|
||||
* NB: ne rajouter $sep que si $s est non vide
|
||||
*/
|
||||
static final function addsep(?string &$s, ?string $sep, ?string $text, ?string $prefix=null, ?string $suffix=null): void {
|
||||
if (!$text) return;
|
||||
if ($s) $s .= $sep;
|
||||
$s .= $prefix.$text.$suffix;
|
||||
}
|
||||
|
||||
/** si $text est non vide, ajouter $prefix$text$suffix à $s en séparant la valeur avec un espace */
|
||||
static final function add(?string &$s, ?string $text, ?string $prefix=null, ?string $suffix=null): void {
|
||||
self::addsep($s, " ", $text, $prefix, $suffix);
|
||||
}
|
||||
|
||||
/** splitter $s en deux chaines séparées par $sep */
|
||||
static final function split_pair(?string $s, string $sep=":"): array {
|
||||
if ($s === null) return [null, null];
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
namespace nur\sery\text;
|
||||
|
||||
use nur\b\ValueException;
|
||||
use nur\txt;
|
||||
use nur\sery\ValueException;
|
||||
use nur\sery\txt;
|
||||
|
||||
/**
|
||||
* Class Word: accord d'un nom ou d'un adjectif en genre et en nombre
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
<?php
|
||||
namespace nur\sery;
|
||||
|
||||
/**
|
||||
* Class str: gestion des chaines de caractère multi-octets
|
||||
*/
|
||||
class txt {
|
||||
/**
|
||||
* Retourner $s converti en chaine non nulle, ou "" si $s est fausse (cela
|
||||
* n'inclue pas la chaine "0")
|
||||
*/
|
||||
static final function with($s): string {
|
||||
if (!is_string($s)) {
|
||||
if (!$s) return "";
|
||||
else $s = strval($s);
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourner $s converti en chaine non nulle, ou "" si $s est fausse selon les
|
||||
* règles de PHP
|
||||
*/
|
||||
static final function pwith($s): string {
|
||||
if (!is_string($s)) {
|
||||
if (!$s) return "";
|
||||
else $s = strval($s);
|
||||
}
|
||||
return $s?: "";
|
||||
}
|
||||
|
||||
/**
|
||||
* tronquer si nécessaire $s à la valeur $length.
|
||||
* la chaine $suffix est rajoutée le cas échéant de façon que la taille
|
||||
* totale n'excède pas $length caractères.
|
||||
*
|
||||
* si $ellips est true et que le troncage est nécessaire, remplacer les 3
|
||||
* derniers caractères par "..."
|
||||
*/
|
||||
static final function trunc(?string $s, int $length, bool $ellips=false, ?string $suffix=null): ?string {
|
||||
if ($s === null) return null;
|
||||
if ($suffix !== null) $length -= mb_strlen($suffix);
|
||||
if (mb_strlen($s) > $length) {
|
||||
if ($ellips && $length > 3) $s = mb_substr($s, 0, $length - 3)."...";
|
||||
else $s = mb_substr($s, 0, $length);
|
||||
}
|
||||
if ($suffix !== null) $s .= $suffix;
|
||||
return $s;
|
||||
}
|
||||
|
||||
/** trimmer $s */
|
||||
static final function trim(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return trim($s);
|
||||
}
|
||||
|
||||
/** trimmer $s à gauche */
|
||||
static final function ltrim(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return ltrim($s);
|
||||
}
|
||||
|
||||
/** trimmer $s à droite */
|
||||
static final function rtrim(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return rtrim($s);
|
||||
}
|
||||
|
||||
static final function left(?string $s, int $size): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_str_pad($s, $size);
|
||||
}
|
||||
|
||||
static final function right(?string $s, int $size): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_str_pad($s, $size, " ", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
static final function center(?string $s, int $size): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_str_pad($s, $size, " ", STR_PAD_BOTH);
|
||||
}
|
||||
|
||||
static final function pad0(?string $s, int $size): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_str_pad($s, $size, "0", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
static final function lower(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_strtolower($s);
|
||||
}
|
||||
|
||||
static final function lower1(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_strtolower(mb_substr($s, 0, 1)).mb_substr($s, 1);
|
||||
}
|
||||
|
||||
static final function upper(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_strtoupper($s);
|
||||
}
|
||||
|
||||
static final function upper1(?string $s): ?string {
|
||||
if ($s === null) return null;
|
||||
return mb_strtoupper(mb_substr($s, 0, 1)).mb_substr($s, 1);
|
||||
}
|
||||
|
||||
static final function upperw(?string $s, ?string $delimiters=null): ?string {
|
||||
if ($s === null) return null;
|
||||
if ($delimiters === null) $delimiters = " _-\t\r\n\f\v";
|
||||
$pattern = "/([".preg_quote($delimiters)."])/u";
|
||||
$words = preg_split($pattern, $s, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$max = count($words) - 1;
|
||||
$ucwords = [];
|
||||
for ($i = 0; $i < $max; $i += 2) {
|
||||
$s = $words[$i];
|
||||
$ucwords[] = mb_strtoupper(mb_substr($s, 0, 1)).mb_substr($s, 1);
|
||||
$ucwords[] = $words[$i + 1];
|
||||
}
|
||||
$s = $words[$max];
|
||||
$ucwords[] = mb_strtoupper(mb_substr($s, 0, 1)).mb_substr($s, 1);
|
||||
return implode("", $ucwords);
|
||||
}
|
||||
|
||||
protected static final function _starts_with(string $prefix, string $s, ?int $min_len=null): bool {
|
||||
if ($prefix === $s) return true;
|
||||
$len = mb_strlen($prefix);
|
||||
if ($min_len !== null && ($len < $min_len || $len > mb_strlen($s))) return false;
|
||||
return $len == 0 || $prefix === mb_substr($s, 0, $len);
|
||||
}
|
||||
|
||||
/**
|
||||
* tester si $s commence par $prefix
|
||||
* par exemple:
|
||||
* - starts_with("", "whatever") est true
|
||||
* - starts_with("fi", "first") est true
|
||||
* - starts_with("no", "yes") est false
|
||||
*
|
||||
* si $min_len n'est pas null, c'est la longueur minimum requise de $prefix
|
||||
* pour qu'on teste la correspondance. dans le cas contraire, la valeur de
|
||||
* retour est toujours false, sauf s'il y a égalité. e.g
|
||||
* - starts_with("a", "abc", 2) est false
|
||||
* - starts_with("a", "a", 2) est true
|
||||
*/
|
||||
static final function starts_with(?string $prefix, ?string $s, ?int $min_len=null): bool {
|
||||
if ($s === null || $prefix === null) return false;
|
||||
else return self::_starts_with($prefix, $s, $min_len);
|
||||
}
|
||||
|
||||
/** Retourner $s sans le préfixe $prefix s'il existe */
|
||||
static final function without_prefix(?string $prefix, ?string $s): ?string {
|
||||
if ($s === null || $prefix === null) return $s;
|
||||
if (self::_starts_with($prefix, $s)) $s = mb_substr($s, mb_strlen($prefix));
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier $s en place pour supprimer le préfixe $prefix s'il existe
|
||||
*
|
||||
* retourner true si le préfixe a été enlevé.
|
||||
*/
|
||||
static final function del_prefix(?string &$s, ?string $prefix): bool {
|
||||
if ($s === null || !self::_starts_with($prefix, $s)) return false;
|
||||
$s = self::without_prefix($prefix, $s);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourner $s avec le préfixe $prefix
|
||||
*
|
||||
* Si $unless_exists, ne pas ajouter le préfixe s'il existe déjà
|
||||
*/
|
||||
static final function with_prefix(?string $prefix, ?string $s, ?string $sep=null, bool $unless_exists=false): ?string {
|
||||
if ($s === null || $prefix === null) return $s;
|
||||
if (!self::_starts_with($prefix, $s) || !$unless_exists) $s = $prefix.$sep.$s;
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier $s en place pour ajouter le préfixe $prefix
|
||||
*
|
||||
* retourner true si le préfixe a été ajouté.
|
||||
*/
|
||||
static final function add_prefix(?string &$s, ?string $prefix, bool $unless_exists=true): bool {
|
||||
if (($s === null || self::_starts_with($prefix, $s)) && $unless_exists) return false;
|
||||
$s = self::with_prefix($prefix, $s, null, $unless_exists);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static final function _ends_with(string $suffix, string $s, ?int $min_len=null): bool {
|
||||
if ($suffix === $s) return true;
|
||||
$len = mb_strlen($suffix);
|
||||
if ($min_len !== null && ($len < $min_len || $len > mb_strlen($s))) return false;
|
||||
return $len == 0 || $suffix === mb_substr($s, -$len);
|
||||
}
|
||||
|
||||
/**
|
||||
* tester si $string se termine par $suffix
|
||||
* par exemple:
|
||||
* - ends_with("", "whatever") est true
|
||||
* - ends_with("st", "first") est true
|
||||
* - ends_with("no", "yes") est false
|
||||
*
|
||||
* si $min_len n'est pas null, c'est la longueur minimum requise de $prefix
|
||||
* pour qu'on teste la correspondance. dans le cas contraire, la valeur de
|
||||
* retour est toujours false, sauf s'il y a égalité. e.g
|
||||
* - ends_with("c", "abc", 2) est false
|
||||
* - ends_with("c", "c", 2) est true
|
||||
*/
|
||||
static final function ends_with(?string $suffix, ?string $s, ?int $min_len=null): bool {
|
||||
if ($s === null || $suffix === null) return false;
|
||||
else return self::_ends_with($suffix, $s, $min_len);
|
||||
}
|
||||
|
||||
/** Retourner $s sans le suffixe $suffix s'il existe */
|
||||
static final function without_suffix(?string $suffix, ?string $s): ?string {
|
||||
if ($s === null || $suffix === null) return $s;
|
||||
if (self::_ends_with($suffix, $s)) $s = mb_substr($s, 0, -mb_strlen($suffix));
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier $s en place pour supprimer le suffixe $suffix s'il existe
|
||||
*
|
||||
* retourner true si le suffixe a été enlevé.
|
||||
*/
|
||||
static final function del_suffix(?string &$s, ?string $suffix): bool {
|
||||
if ($s === null || !self::_ends_with($suffix, $s)) return false;
|
||||
$s = self::without_suffix($suffix, $s);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourner $s avec le suffixe $suffix
|
||||
*
|
||||
* Si $unless_exists, ne pas ajouter le suffixe s'il existe déjà
|
||||
*/
|
||||
static final function with_suffix(?string $suffix, ?string $s, ?string $sep=null, bool $unless_exists=false): ?string {
|
||||
if ($s === null || $suffix === null) return $s;
|
||||
if (!self::_ends_with($suffix, $s) || !$unless_exists) $s = $s.$sep.$suffix;
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* modifier $s en place pour ajouter le suffixe $suffix
|
||||
*
|
||||
* retourner true si le suffixe a été ajouté.
|
||||
*/
|
||||
static final function add_suffix(?string &$s, ?string $suffix, bool $unless_exists=true): bool {
|
||||
if (($s === null || self::_ends_with($suffix, $s)) && $unless_exists) return false;
|
||||
$s = self::with_suffix($suffix, $s, null, $unless_exists);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ajouter $sep$prefix$text$suffix à $s si $text est non vide
|
||||
*
|
||||
* NB: ne rajouter $sep que si $s est non vide
|
||||
*/
|
||||
static final function addsep(?string &$s, ?string $sep, ?string $text, ?string $prefix=null, ?string $suffix=null): void {
|
||||
if (!$text) return;
|
||||
if ($s) $s .= $sep;
|
||||
$s .= $prefix.$text.$suffix;
|
||||
}
|
||||
|
||||
/** si $text est non vide, ajouter $prefix$text$suffix à $s en séparant la valeur avec un espace */
|
||||
static final function add(?string &$s, ?string $text, ?string $prefix=null, ?string $suffix=null): void {
|
||||
self::addsep($s, " ", $text, $prefix, $suffix);
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# divers
|
||||
|
||||
/**
|
||||
* supprimer les diacritiques de la chaine $text
|
||||
*
|
||||
* la translitération se fait avec les règles de la locale spécifiée.
|
||||
* NB: la translitération ne fonctionne pas si LC_CTYPE == C ou POISX
|
||||
*/
|
||||
static final function remove_diacritics(?string $text, string $locale="fr_FR.UTF-8"): ?string {
|
||||
if ($text === null) return null;
|
||||
#XXX est-ce thread-safe?
|
||||
$olocale = setlocale(LC_CTYPE, 0);
|
||||
try {
|
||||
setlocale(LC_CTYPE, $locale);
|
||||
$clean = @iconv("UTF-8", "US-ASCII//TRANSLIT", $text);
|
||||
if ($clean === false) $clean = "";
|
||||
return $clean;
|
||||
} finally {
|
||||
setlocale(LC_CTYPE, $olocale);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,20 @@
|
|||
<?php
|
||||
namespace nur\sery\app;
|
||||
namespace nur\sery\wip\app;
|
||||
#XXX déplacer dans nur\sery\app dès que la dépendance sur nur\cli\Application sera levée
|
||||
|
||||
use nur\cli\Application;
|
||||
use nur\sery\app\LockFile;
|
||||
use nur\sery\app\RunFile;
|
||||
use nur\sery\os\path;
|
||||
use nur\sery\os\sh;
|
||||
use nur\sery\ValueException;
|
||||
|
||||
class app {
|
||||
private static function isa_Application($app): bool {
|
||||
if (!is_string($app)) return false;
|
||||
return $app === Application::class || is_subclass_of($app, Application::class);
|
||||
}
|
||||
|
||||
/** @param Application|string */
|
||||
static function with($app): self {
|
||||
if ($app instanceof Application) {
|
||||
|
@ -18,7 +26,7 @@ class app {
|
|||
$etcdir = $app::ETCDIR;
|
||||
$vardir = $app::VARDIR;
|
||||
$logdir = $app::LOGDIR;
|
||||
} elseif (is_string($app) && is_subclass_of($app, Application::class)) {
|
||||
} elseif (self::isa_Application($app)) {
|
||||
$projdir = constant("$app::PROJDIR");
|
||||
$appcode = constant("$app::APPCODE");
|
||||
$name = constant("$app::NAME");
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
namespace nur\sery\wip\app;
|
||||
|
||||
use Exception;
|
||||
use nur\b\proc\Cmd;
|
||||
use nur\sery\app\RunFile;
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\StateException;
|
||||
use nur\yaml;
|
||||
|
||||
class launcher {
|
||||
static function launch(string $appClass, ...$args): int {
|
||||
$cmd = new Cmd([
|
||||
__DIR__."/../../../lib/launch.php",
|
||||
$appClass,
|
||||
...$args,
|
||||
]);
|
||||
$cmd->addRedir("null");
|
||||
$cmd->passthru($exitcode);
|
||||
return $exitcode;
|
||||
}
|
||||
|
||||
static function _start(array $args, Runfile $runfile): void {
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
# parent, impossible de forker
|
||||
throw new StateException("unable to fork");
|
||||
} elseif ($pid) {
|
||||
# parent, fork ok
|
||||
} else {
|
||||
## child, fork ok
|
||||
# Créer un groupe de process, pour pouvoir les tuer toutes en même temps
|
||||
$runfile->tm_startPg();
|
||||
$exitcode = -776;
|
||||
try {
|
||||
# puis lancer la commande
|
||||
$cmd = new Cmd($args);
|
||||
#XXX fichier de log?
|
||||
$cmd->addRedir("null");
|
||||
$cmd->fork_exec($exitcode);
|
||||
} finally {
|
||||
$runfile->stopped($exitcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function _stop(Runfile $runfile): void {
|
||||
$data = $runfile->read();
|
||||
$pid = $data["pg_pid"];
|
||||
if ($pid === null) {
|
||||
msg::warning("$data[name]: groupe de process inconnu");
|
||||
return;
|
||||
}
|
||||
msg::action("kill $pid");
|
||||
if (!posix_kill(-$pid, SIGKILL)) {
|
||||
switch (posix_get_last_error()) {
|
||||
case PCNTL_ESRCH:
|
||||
msg::afailure("process inexistant");
|
||||
break;
|
||||
case PCNTL_EPERM:
|
||||
msg::afailure("process non accessible");
|
||||
break;
|
||||
case PCNTL_EINVAL:
|
||||
msg::afailure("signal invalide");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$timeout = 10;
|
||||
while ($runfile->tm_isUndead($pid)) {
|
||||
sleep(1);
|
||||
if (--$timeout == 0) {
|
||||
msg::afailure("impossible d'arrêter la tâche");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$runfile->stopped(-778);
|
||||
msg::asuccess();
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
/output-forever.log
|
||||
/devel/
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
require(__DIR__.'/../vendor/autoload.php');
|
||||
|
||||
use nur\sery\app\LongTaskApp;
|
||||
|
||||
LongTaskApp::run();
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
namespace nur\sery\app;
|
||||
|
||||
use nur\cli\Application;
|
||||
use nur\sery\output\msg;
|
||||
|
||||
class LongTaskApp extends Application {
|
||||
const APPCODE = "long-task";
|
||||
const USE_LOGFILE = true;
|
||||
const USE_RUNFILE = true;
|
||||
const USE_RUNLOCK = true;
|
||||
|
||||
function main() {
|
||||
$step = 100;
|
||||
while (--$step > 0) {
|
||||
msg::print("step $step");
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace nur\sery\app;
|
||||
|
||||
use nulib\tests\TestCase;
|
||||
|
||||
class launcherTest extends TestCase {
|
||||
}
|
Loading…
Reference in New Issue