1121 lines
38 KiB
PHP
1121 lines
38 KiB
PHP
|
<?php
|
||
|
namespace nur\m\pdo\mysql;
|
||
|
|
||
|
use Exception;
|
||
|
use nur\A;
|
||
|
use nur\b\ValueException;
|
||
|
use nur\base;
|
||
|
use nur\func;
|
||
|
use nur\m\base\AbstractRow;
|
||
|
use nur\md;
|
||
|
use nur\php\SrcGenerator;
|
||
|
use nur\str;
|
||
|
use ReflectionClass;
|
||
|
|
||
|
/**
|
||
|
* Class MysqlMigrations
|
||
|
*
|
||
|
* Dans la classe destination (celle à partir de laquelle sont générées les
|
||
|
* migrations), les constantes suivantes peuvent être définies:
|
||
|
* - DBTRAIT: classe qui est utilisée comme dbtrait pour *toutes* les migrations
|
||
|
* - DEFAULT_DBTRAIT: classe qui est utilisée comme dbtrait pour les migrations
|
||
|
* qui n'en définissent pas
|
||
|
* - MERGE: une classe XxxMigration qu'il faut aussi considérer
|
||
|
* - MERGES: une liste de classes XxxMigration qu'il faut aussi considérer.
|
||
|
*
|
||
|
* Les constantes MERGE et MERGES sont calculées récursivement. Les constantes
|
||
|
* DBTRAIT et DEFAULT_DBTRAIT ne sont consultées que dans la classe destination.
|
||
|
*/
|
||
|
class MysqlMigrations {
|
||
|
const UPDATE_DIRECTIVES = [
|
||
|
"+name", # nom de base du fichier sql généré
|
||
|
"+desc", # description à insérer dans le fichier sql
|
||
|
"+dbtrait", # trait qui fourni verifix_conn(). Si cette valeur n'est pas fournie, la constante DBTRAIT de la classe destination donne la valeur par défaut
|
||
|
"+prod", # cette configuration a-t-elle été déployée en prod? (ne plus modifier)
|
||
|
];
|
||
|
const TABLE_DIRECTIVES = [
|
||
|
"+name", # nom effectif de la table
|
||
|
"+desc", # description à insérer en commentaire
|
||
|
"+type", # type: table (par défaut) ou view
|
||
|
# directives spécifiques aux tables
|
||
|
"+suffix", # suffixe des colonnes de cette table
|
||
|
"+primary key",
|
||
|
"+foreign key",
|
||
|
"+index",
|
||
|
"+unique index",
|
||
|
# directives spécifiques aux vues
|
||
|
"+view_tables", # tables participant aux vues
|
||
|
"+view_def", # définition de la vue
|
||
|
];
|
||
|
|
||
|
###
|
||
|
|
||
|
private static $trace;
|
||
|
private static function trace($msg) {
|
||
|
if (self::$trace !== null) func::call(self::$trace, $msg);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* tester si le fichier doit être écrasé
|
||
|
*
|
||
|
* si $overwrite n'est pas booléen, ce doit être un nombre maximum de ligne au
|
||
|
* delà duquel le fichier n'est pas écrasé
|
||
|
*/
|
||
|
private static function should_overwrite($file, $overwrite) {
|
||
|
if (!is_int($overwrite)) $overwrite = boolval($overwrite);
|
||
|
if ($overwrite === true || $overwrite === false) return $overwrite;
|
||
|
if (!is_file($file)) return true;
|
||
|
$inf = fopen($file, "rb");
|
||
|
try {
|
||
|
while ($overwrite > 0) {
|
||
|
$line = fgets($inf);
|
||
|
if ($line === false) return true;
|
||
|
$overwrite--;
|
||
|
}
|
||
|
$line = fgets($inf);
|
||
|
return $line === false || $line === "";
|
||
|
} finally {
|
||
|
fclose($inf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static function get_ovsuf($file, $overwrite) {
|
||
|
if (is_file($file)) {
|
||
|
if ($overwrite) return " (OVERWRITE)";
|
||
|
else return "";
|
||
|
} else return " (NEW)";
|
||
|
}
|
||
|
|
||
|
private static function write($line, $w, $tmpf) {
|
||
|
$r = fwrite($w, $line);
|
||
|
if ($r === false) {
|
||
|
fclose($w);
|
||
|
throw new Exception("$tmpf: erreur lors de l'écriture du fichier");
|
||
|
}
|
||
|
}
|
||
|
private static function write_lines($lines, $outf, $overwrite=true, $final_nl=true) {
|
||
|
if (is_file($outf) && !$overwrite) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$dir = dirname($outf);
|
||
|
if (!is_dir($dir)) {
|
||
|
$r = mkdir($dir, 0777, true);
|
||
|
if ($r === false) {
|
||
|
throw new Exception("$dir: erreur lors de la création du répertoire");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$tmpf = "$outf.tmp";
|
||
|
$w = fopen($tmpf, "w+b");
|
||
|
if ($w === false) {
|
||
|
throw new Exception("$tmpf: erreur d'ouverture du fichier");
|
||
|
}
|
||
|
$first = true;
|
||
|
foreach ($lines as $line) {
|
||
|
if ($first) $first = false;
|
||
|
else self::write("\n", $w, $tmpf);
|
||
|
self::write($line, $w, $tmpf);
|
||
|
}
|
||
|
if ($final_nl) self::write("\n", $w, $tmpf);
|
||
|
$r = fclose($w);
|
||
|
if ($r === false) {
|
||
|
throw new Exception("$tmpf: erreur lors de la fermeture du fichier");
|
||
|
}
|
||
|
$r = rename($tmpf, $outf);
|
||
|
if ($r === false) {
|
||
|
throw new Exception("$outf: erreur lors du renommage du fichier");
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
###
|
||
|
|
||
|
private static function is_update(string $method) {
|
||
|
return str::starts_with("update", $method);
|
||
|
}
|
||
|
private static function get_index(string $method) {
|
||
|
return str::without_prefix("update", $method);
|
||
|
}
|
||
|
private static function update_methods(array &$methods, string $class): void {
|
||
|
$all_methods = get_class_methods($class);
|
||
|
if ($all_methods === null) {
|
||
|
throw new Exception("$class: classe introuvable");
|
||
|
}
|
||
|
$prefix = str_replace("\\", "_", $class);
|
||
|
foreach ($all_methods as $method) {
|
||
|
if (!self::is_update($method)) continue;
|
||
|
$index = self::get_index($method);
|
||
|
$name = "${prefix}_$index";
|
||
|
$methods[$name] = [
|
||
|
"class" => $class,
|
||
|
"name" => $method,
|
||
|
"index" => $index,
|
||
|
"method" => [$class, $method],
|
||
|
"prefix" => $prefix,
|
||
|
];
|
||
|
}
|
||
|
$merge_classes = [];
|
||
|
$c = new ReflectionClass($class);
|
||
|
$merges = $c->getConstant("MERGES");
|
||
|
if (base::nz($merges)) A::merge($merge_classes, $merges);
|
||
|
$merge = $c->getConstant("MERGE");
|
||
|
if (base::nz($merge)) $merge_classes[] = $merge;
|
||
|
foreach ($merge_classes as $class) {
|
||
|
self::update_methods($methods, $class);
|
||
|
}
|
||
|
}
|
||
|
private static function get_methods($class) {
|
||
|
$methods = [];
|
||
|
self::update_methods($methods, $class);
|
||
|
ksort($methods, SORT_NATURAL);
|
||
|
return $methods;
|
||
|
}
|
||
|
|
||
|
###
|
||
|
|
||
|
private static function parse_col($def): array {
|
||
|
if (preg_match('/^([a-zA-Z0-9_]+)(?:\((\d+)\s*(?:,\s*(\d+))?\))?\s*(.*)\s*(?:--\s*(.*)\s*)?(?:--\s*(.*)\s*)?$/', $def, $ms)) {
|
||
|
$size = base::vn(A::get($ms, 2));
|
||
|
if ($size !== null) $size = intval($size);
|
||
|
$precision = base::vn(A::get($ms, 3));
|
||
|
if ($precision !== null) $precision = intval($precision);
|
||
|
return [
|
||
|
"type" => A::get($ms, 1),
|
||
|
"size" => $size,
|
||
|
"precision" => $precision,
|
||
|
"options" => base::vn(A::get($ms, 4)),
|
||
|
"title" => base::vn(A::get($ms, 5)),
|
||
|
"desc" => base::vn(A::get($ms, 6)),
|
||
|
];
|
||
|
} else {
|
||
|
throw new ValueException("erreur de syntaxe: $def");
|
||
|
}
|
||
|
}
|
||
|
private static function is_multi_fkdef($def, &$ms) {
|
||
|
return preg_match('/^\((.+)\)$/', $def, $ms);
|
||
|
}
|
||
|
private static function is_single_fkref($ref, &$ms) {
|
||
|
return preg_match('/^([a-zA-Z0-9_]+)\(([a-zA-Z0-9_]+)\)$/', $ref, $ms);
|
||
|
}
|
||
|
|
||
|
private static $tables = [];
|
||
|
|
||
|
private static function has_table($table) {
|
||
|
return A::get(self::$tables, $table, false) !== false;
|
||
|
}
|
||
|
private static function has_col($table, $col) {
|
||
|
return (self::has_table($table) &&
|
||
|
A::get(self::$tables[$table], $col, false) !== false);
|
||
|
}
|
||
|
private static function add_table($table, $suffix=null, $dbtrait=null, $name=null): ?array {
|
||
|
if (self::has_table($table)) return null;
|
||
|
if ($name === null) $name = $table;
|
||
|
return self::$tables[$table] = [
|
||
|
"name" => $name,
|
||
|
"suffix" => $suffix,
|
||
|
"cols" => [],
|
||
|
"pk" => null,
|
||
|
"dbtrait" => $dbtrait,
|
||
|
];
|
||
|
}
|
||
|
private static function set_table_pk($table, $pk) {
|
||
|
self::add_table($table);
|
||
|
self::$tables[$table]["pk"] = A::with($pk);
|
||
|
}
|
||
|
private static function add_col($table, $col, $def=null): array {
|
||
|
self::add_table($table);
|
||
|
if ($def !== null) {
|
||
|
[
|
||
|
"type" => $type, "size" => $size, "precision" => $precision,
|
||
|
"options" => $options,
|
||
|
"title" => $title, "desc" => $desc,
|
||
|
] = self::parse_col($def);
|
||
|
#XXX calculer le type du schema: bool si integer(1), integer si integer, etc.
|
||
|
|
||
|
$pk_col = boolval(preg_match('/\bprimary\s+key\b/i', $options));
|
||
|
$auto_col = boolval(preg_match('/\bauto_increment\b/i', $options));
|
||
|
if (str::starts_with("datetime", $type)) {
|
||
|
$hot_type = "date"; #XXX ajouter le support datetime
|
||
|
} elseif (str::starts_with("date", $type)) {
|
||
|
$hot_type = "date";
|
||
|
} elseif (str::starts_with("int", $type)) {
|
||
|
$hot_type = "numeric";
|
||
|
} else {
|
||
|
$hot_type = "text";
|
||
|
}
|
||
|
|
||
|
$cinfos = [
|
||
|
"name" => $col,
|
||
|
"def" => $def,
|
||
|
"pk" => $pk_col,
|
||
|
"auto" => $auto_col,
|
||
|
"hot_type" => $hot_type,
|
||
|
#
|
||
|
"schema" => [
|
||
|
"name" => $col,
|
||
|
"title" => $title,
|
||
|
"desc" => $desc,
|
||
|
"mysql" => [
|
||
|
"type" => $type,
|
||
|
"size" => $size,
|
||
|
"precision" => $precision,
|
||
|
"options" => $options,
|
||
|
"pk" => $pk_col,
|
||
|
"auto" => $auto_col,
|
||
|
],
|
||
|
"hot" => [
|
||
|
"type" => $hot_type,
|
||
|
],
|
||
|
],
|
||
|
];
|
||
|
} else {
|
||
|
$cinfos = [
|
||
|
"name" => $col,
|
||
|
];
|
||
|
}
|
||
|
return self::$tables[$table]["cols"][$col] = $cinfos;
|
||
|
}
|
||
|
private static function del_table($table) {
|
||
|
unset(self::$tables[$table]);
|
||
|
}
|
||
|
private static function del_col($table, $col) {
|
||
|
if (!self::has_table($table)) return;
|
||
|
unset(self::$tables[$table]["cols"][$col]);
|
||
|
}
|
||
|
|
||
|
private static $views = [];
|
||
|
|
||
|
private static function has_view($view) {
|
||
|
return A::get(self::$views, $view, false) !== false;
|
||
|
}
|
||
|
private static function add_view($view, $tables, $dbtrait=null, $name=null) {
|
||
|
if (self::has_view($view)) return;
|
||
|
if ($name === null) $name = $view;
|
||
|
self::$views[$view] = [
|
||
|
"name" => $name,
|
||
|
"tables" => $tables,
|
||
|
"dbtrait" => $dbtrait,
|
||
|
];
|
||
|
}
|
||
|
private static function del_view($view) {
|
||
|
unset(self::$views[$view]);
|
||
|
}
|
||
|
|
||
|
private static $t1links = [];
|
||
|
private static $tmlinks = [];
|
||
|
|
||
|
private static function add_t1link($scol, $stable, $dcol, $dtable) {
|
||
|
self::$t1links[] = [$scol, $stable, $dcol, $dtable];
|
||
|
}
|
||
|
|
||
|
private static function reset($options=null, $tables_only=false) {
|
||
|
self::$tables = [];
|
||
|
self::$views = [];
|
||
|
self::$t1links = [];
|
||
|
self::$tmlinks = [];
|
||
|
if (!$tables_only) {
|
||
|
self::$trace = A::get($options, "trace");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
###
|
||
|
|
||
|
static function gensql($class, $options=null) {
|
||
|
self::reset($options);
|
||
|
$destdir = A::get($options, "destdir", ".");
|
||
|
$overwrite = A::get($options, "overwrite");
|
||
|
$verbose = A::get($options, "verbose");
|
||
|
$prefix = A::get($options, "prefix");
|
||
|
|
||
|
self::trace("# gensql");
|
||
|
self::trace(" class: $class");
|
||
|
self::trace(" destdir: $destdir");
|
||
|
|
||
|
$methods = self::get_methods($class);
|
||
|
foreach ($methods as $minfos) {
|
||
|
self::reset(null, true);
|
||
|
$update = func::call($minfos["method"]);
|
||
|
|
||
|
# par défaut, ne pas écraser les fichiers qui sont en +prod
|
||
|
$prod = A::get($update, "+prod");
|
||
|
$overwrite_sql = $overwrite === null? !$prod: $overwrite;
|
||
|
|
||
|
self::trace(" ## $minfos[class]::$minfos[name]");
|
||
|
|
||
|
$lines = [
|
||
|
"-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8",
|
||
|
"-- fichier autogénéré: toute modification locale risque d'être perdue",
|
||
|
];
|
||
|
$desc = A::get($update, "+desc");
|
||
|
if ($desc) $lines[] = "-- $desc";
|
||
|
|
||
|
foreach ($update as $uname => $uinfos) {
|
||
|
# ignorer les directives de mise à jour: elles ont été traitées ci-dessus
|
||
|
if (str::starts_with("+", $uname)) {
|
||
|
if (!in_array($uname, self::UPDATE_DIRECTIVES)) {
|
||
|
trigger_error("$uname: unknown update directive", E_USER_WARNING);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$type = A::get($uinfos, "+type", "table");
|
||
|
if ($type === "table") {
|
||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
$table = $uname;
|
||
|
$table_name = A::get($uinfos, "+name", $table);
|
||
|
$cols = $uinfos;
|
||
|
|
||
|
#####################################################################
|
||
|
# suppression de table
|
||
|
if ($cols === null) {
|
||
|
$lines[] = "drop table $table_name;";
|
||
|
self::del_table($table);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# liste de champs "normale"
|
||
|
if (!self::has_table($table)) {
|
||
|
###################################################################
|
||
|
# nouvelle table
|
||
|
$lines[] = "";
|
||
|
$desc = A::get($cols, "+desc");
|
||
|
if ($desc) $lines[] = "-- $desc";
|
||
|
$lines[] = "create table $table_name (";
|
||
|
$suffix = A::get($cols, "+suffix");
|
||
|
self::add_table($table, $suffix, null, $table_name);
|
||
|
$first = true;
|
||
|
foreach ($cols as $col => $def) {
|
||
|
# les directives seront traitées plus tard
|
||
|
if (str::starts_with("+", $col)) continue;
|
||
|
$lines[] = ($first? " ": ",")."$col $def";
|
||
|
$cinfos = self::add_col($table, $col, $def);
|
||
|
if ($cinfos["pk"]) self::set_table_pk($table, $col);
|
||
|
$first = false;
|
||
|
}
|
||
|
foreach ($cols as $col => $value) {
|
||
|
if (!str::starts_with("+", $col)) continue;
|
||
|
switch ($col) {
|
||
|
case "+primary key":
|
||
|
$pk = A::with($value);
|
||
|
self::set_table_pk($table, $pk);
|
||
|
$pk = implode(", ", $pk);
|
||
|
$lines[] = ",primary key ($pk)";
|
||
|
break;
|
||
|
case "+foreign key":
|
||
|
foreach (A::with($value) as $fk => $ref) {
|
||
|
$ms = null;
|
||
|
if (self::is_multi_fkdef($fk, $ms)) $fk = $ms[1];
|
||
|
$lines[] = ",foreign key ($fk) references $ref";
|
||
|
$ms = null;
|
||
|
if (self::is_single_fkref($ref, $ms)) {
|
||
|
self::add_t1link($fk, $table, $ms[2], $ms[1]);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case "+index":
|
||
|
foreach (A::with($value) as $name => $cols) {
|
||
|
if (is_numeric($name)) $name = false;
|
||
|
$cols = implode(", ", A::with($cols));
|
||
|
$lines[] = ",index $name($cols)";
|
||
|
}
|
||
|
break;
|
||
|
case "+unique index":
|
||
|
foreach (A::with($value) as $name => $cols) {
|
||
|
if (is_numeric($name)) $name = false;
|
||
|
$cols = implode(", ", A::with($cols));
|
||
|
$lines[] = ",unique index $name($cols)";
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (!in_array($col, self::TABLE_DIRECTIVES)) {
|
||
|
trigger_error("$table: $col: unknown table directive", E_USER_WARNING);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$lines[] = ");";
|
||
|
} else {
|
||
|
###################################################################
|
||
|
# maj d'une table existante
|
||
|
$lines[] = "";
|
||
|
$desc = A::get($cols, "+desc");
|
||
|
if ($desc) $lines[] = "-- $desc";
|
||
|
foreach ($cols as $col => $def) {
|
||
|
# les directives seront traitées plus tard
|
||
|
if (!str::starts_with("+", $col)) {
|
||
|
if ($def === null) {
|
||
|
## suppression d'un champ
|
||
|
if (!self::has_col($table, $col)) {
|
||
|
trigger_error("$table.$col: deleting a non-existent column", E_USER_WARNING);
|
||
|
}
|
||
|
$lines[] = "alter table table_name drop column $col;";
|
||
|
self::del_col($table, $col);
|
||
|
} else {
|
||
|
if (self::has_col($table, $col)) {
|
||
|
## maj d'un champ
|
||
|
$lines[] = "alter table table_name change column $col $col $def;";
|
||
|
# NB: pour simplifier, ne pas tenir compte du fait que ce
|
||
|
# nouveau champ pourrait maintenant être une clé primaire
|
||
|
} else {
|
||
|
## ajout d'un champ
|
||
|
$lines[] = "alter table table_name add column $col $def;";
|
||
|
self::add_col($table, $col, $def);
|
||
|
# NB: pour simplifier, ne pas tenir compte du fait que ce
|
||
|
# nouveau champ pourrait être une clé primaire
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
## maj directive
|
||
|
trigger_error("$table: $col: table directive update is not (yet) supported", E_USER_WARNING);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} elseif ($type === "view") {
|
||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
$view = $uname;
|
||
|
$view_name = A::get($uinfos, "+name", $view);
|
||
|
$vinfos = $uinfos;
|
||
|
|
||
|
#####################################################################
|
||
|
# suppression de vue
|
||
|
if ($vinfos === null) {
|
||
|
$lines[] = "drop view $view_name;";
|
||
|
self::del_view($view);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# définition "normale"
|
||
|
if (!self::has_view($view)) {
|
||
|
###################################################################
|
||
|
# nouvelle vue
|
||
|
$tables = A::get($vinfos, "+view_tables");
|
||
|
$def = A::get($vinfos, "+view_def");
|
||
|
if ($def === null) trigger_error("$view: missing +view_def", E_USER_WARNING);
|
||
|
self::add_view($view, $tables, null, $view_name);
|
||
|
|
||
|
$lines[] = "";
|
||
|
$desc = A::get($vinfos, "+desc");
|
||
|
if ($desc) $lines[] = "-- $desc";
|
||
|
$lines[] = "create view $view_name as";
|
||
|
$lines[] = $def;
|
||
|
$lines[] = ";";
|
||
|
} else {
|
||
|
###################################################################
|
||
|
# maj d'une vue existante
|
||
|
trigger_error("$view: view update is not (yet) supported", E_USER_WARNING);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
trigger_error("$type: unknown type, expected table or view", E_USER_WARNING);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (self::$tables || self::$views) {
|
||
|
$name = A::get($update, "+name", "update");
|
||
|
$index = $minfos["index"];
|
||
|
$outfname = "$index-$name.sql";
|
||
|
if ($prefix) $outfname = "$minfos[prefix]--$outfname";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite_sql);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" sql output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, "$outf", $overwrite_sql);
|
||
|
}
|
||
|
|
||
|
# les fichiers csv ne sont écrasés que s'ils n'ont pas de données
|
||
|
# automatiquement
|
||
|
$overwrite_csv_threshold = $overwrite_sql? 1: false;
|
||
|
foreach (self::$tables as $table => $tinfos) {
|
||
|
$table_name = $tinfos["name"];
|
||
|
$cols = array_keys($tinfos["cols"]);
|
||
|
$lines = [implode(",", $cols)];
|
||
|
$index++;
|
||
|
$outfname = "$index-$table_name.csv";
|
||
|
if ($prefix) $outfname = "$minfos[prefix]--$outfname";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$overwrite_csv = self::should_overwrite($outf, $overwrite_csv_threshold);
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite_csv);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" csv output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, "$outf", $overwrite_csv);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static function gendoc($class, $options=null) {
|
||
|
self::reset($options);
|
||
|
$destdir = A::get($options, "destdir", ".");
|
||
|
$overwrite = A::get($options, "overwrite");
|
||
|
$verbose = A::get($options, "verbose");
|
||
|
|
||
|
# par défaut, écraser les fichiers lors de la génération de la doc
|
||
|
if ($overwrite === null) $overwrite = true;
|
||
|
|
||
|
self::trace("# gendoc");
|
||
|
self::trace(" class: $class");
|
||
|
self::trace(" destdir: $destdir");
|
||
|
|
||
|
$methods = self::get_methods($class);
|
||
|
foreach ($methods as $minfos) {
|
||
|
$update = func::call($minfos["method"]);
|
||
|
|
||
|
foreach ($update as $uname => $uinfos) {
|
||
|
# ignorer les directives de mise à jour: elles ont été traitées ci-dessus
|
||
|
if (str::starts_with("+", $uname)) {
|
||
|
if (!in_array($uname, self::UPDATE_DIRECTIVES)) {
|
||
|
trigger_error("$uname: unknown update directive", E_USER_WARNING);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$type = A::get($uinfos, "+type", "table");
|
||
|
if ($type === "table") {
|
||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
$table = $uname;
|
||
|
$cols = $uinfos;
|
||
|
|
||
|
#####################################################################
|
||
|
# suppression de table
|
||
|
if ($cols === null) {
|
||
|
self::del_table($table);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# liste de champs "normale"
|
||
|
if (!self::has_table($table)) {
|
||
|
###################################################################
|
||
|
# nouvelle table
|
||
|
self::add_table($table);
|
||
|
foreach ($cols as $col => $value) {
|
||
|
if (!str::starts_with("+", $col)) {
|
||
|
self::add_col($table, $col);
|
||
|
} else {
|
||
|
switch ($col) {
|
||
|
case "+foreign key":
|
||
|
foreach (A::with($value) as $fk => $ref) {
|
||
|
$ms = null;
|
||
|
if (self::is_multi_fkdef($fk, $ms)) $fk = $ms[1];
|
||
|
if (self::is_single_fkref($ref, $ms)) {
|
||
|
self::add_t1link($fk, $table, $ms[2], $ms[1]);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (!in_array($col, self::TABLE_DIRECTIVES)) {
|
||
|
trigger_error("$table: $col: unknown table directive", E_USER_WARNING);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
###################################################################
|
||
|
# maj d'une table existante
|
||
|
foreach ($cols as $col => $def) {
|
||
|
# les directives seront traitées plus tard
|
||
|
if (!str::starts_with("+", $col)) {
|
||
|
if ($def === null) {
|
||
|
## suppression d'un champ
|
||
|
if (!self::has_col($table, $col)) {
|
||
|
trigger_error("$table.$col: deleting a non-existent column", E_USER_WARNING);
|
||
|
}
|
||
|
self::del_col($table, $col);
|
||
|
} else {
|
||
|
if (!self::has_col($table, $col)) {
|
||
|
self::add_col($table, $col);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
## maj directive
|
||
|
trigger_error("$table: $col: table directive update is not (yet) supported", E_USER_WARNING);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} elseif ($type === "view") {
|
||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
$view = $uname;
|
||
|
$vinfos = $uinfos;
|
||
|
|
||
|
#####################################################################
|
||
|
# suppression de vue
|
||
|
if ($vinfos === null) {
|
||
|
self::del_view($view);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# définition "normale"
|
||
|
if (!self::has_view($view)) {
|
||
|
###################################################################
|
||
|
# nouvelle vue
|
||
|
$tables = A::get($vinfos, "+view_tables", null);
|
||
|
self::add_view($view, $tables);
|
||
|
|
||
|
} else {
|
||
|
###################################################################
|
||
|
# maj d'une vue existante
|
||
|
trigger_error("$view: view update is not (yet) supported", E_USER_WARNING);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
trigger_error("$type: unknown type, expected table or view", E_USER_WARNING);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$lines = [
|
||
|
"# -*- coding: utf-8 mode: plantuml -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8",
|
||
|
"# fichier autogénéré: toute modification locale risque d'être perdue",
|
||
|
"",
|
||
|
"##@startuml",
|
||
|
"hide empty members",
|
||
|
"hide empty methods",
|
||
|
"hide circle",
|
||
|
"!ifndef SHOW_DETAILS",
|
||
|
"hide members",
|
||
|
"!endif",
|
||
|
];
|
||
|
foreach (self::$tables as $table => $tinfos) {
|
||
|
$lines[] = "";
|
||
|
$lines[] = "class $table {";
|
||
|
$cols = $tinfos["cols"];
|
||
|
foreach (array_keys($cols) as $col) {
|
||
|
$lines[] = " $col";
|
||
|
}
|
||
|
$lines[] = "}";
|
||
|
}
|
||
|
foreach (self::$views as $view => $vinfos) {
|
||
|
$lines[] = "";
|
||
|
$lines[] = "class $view<view> {";
|
||
|
$tables = $vinfos["tables"];
|
||
|
foreach ($tables as $table) {
|
||
|
$cols = self::$tables[$table]["cols"];
|
||
|
foreach (array_keys($cols) as $col) {
|
||
|
$lines[] = " $col";
|
||
|
}
|
||
|
}
|
||
|
$lines[] = "}";
|
||
|
}
|
||
|
$lines[] = "";
|
||
|
$lines[] = "##@enduml";
|
||
|
|
||
|
$outfname = "classes.iuml";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" puml output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, $outf, $overwrite, true);
|
||
|
}
|
||
|
|
||
|
$lines = [];
|
||
|
foreach (self::$tables as $table => $tinfos) {
|
||
|
$lines[] = "'hide $table";
|
||
|
}
|
||
|
$first = true;
|
||
|
foreach (self::$t1links as $link) {
|
||
|
if ($first) $lines[] = "";
|
||
|
list($scol, $stable, $dcol, $dtable) = $link;
|
||
|
$lines[] = "$stable \"*\" -- \"1\" $dtable";
|
||
|
$first = false;
|
||
|
}
|
||
|
A::merge($lines, [
|
||
|
"~~~",
|
||
|
"",
|
||
|
"-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary",
|
||
|
]);
|
||
|
|
||
|
$slines = [
|
||
|
"![Modèle simplifié](model-simple.png)",
|
||
|
"~~~puml",
|
||
|
"# output=model-simple.png",
|
||
|
"!include classes.iuml",
|
||
|
];
|
||
|
A::merge($slines, $lines);
|
||
|
$outfname = "model-simple.md";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" md output: $outfname$ovsuf");
|
||
|
self::write_lines($slines, $outf, $overwrite, false);
|
||
|
}
|
||
|
|
||
|
$dlines = [
|
||
|
"![Modèle détaillé](model-details.png)",
|
||
|
"~~~puml",
|
||
|
"# output=model-details.png",
|
||
|
"!define SHOW_DETAILS",
|
||
|
"!include classes.iuml",
|
||
|
];
|
||
|
A::merge($dlines, $lines);
|
||
|
$outfname = "model-details.md";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" md output: $outfname$ovsuf");
|
||
|
self::write_lines($dlines, $outf, $overwrite, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static function add_vtables(&$lines, $cols) {
|
||
|
$lines[] = "";
|
||
|
$lines[] = " const VTABLES_HEADERS = self::COLUMNS;";
|
||
|
foreach ($cols as $col => $cinfos) {
|
||
|
$vcol = strtoupper($col)."_VCOL";
|
||
|
$lines[] = " const $vcol = [";
|
||
|
$lines[] = " \"data\" => \"$col\",";
|
||
|
$lines[] = " \"type\" => \"$cinfos[hot_type]\",";
|
||
|
if ($cinfos["hot_type"] == "date") {
|
||
|
$lines[] = " \"dateFormat\" => \"DD/MM/YYYY\",";
|
||
|
$lines[] = " \"correctFormat\" => true,";
|
||
|
}
|
||
|
$lines[] = " ];";
|
||
|
}
|
||
|
$lines[] = " const VTABLES_SCHEMA = [";
|
||
|
foreach (array_keys($cols) as $col) {
|
||
|
$vcol = strtoupper($col)."_VCOL";
|
||
|
$lines[] = " self::$vcol,";
|
||
|
}
|
||
|
$lines[] = " ];";
|
||
|
}
|
||
|
private static function add_data_schema(&$lines, $cols) {
|
||
|
$cols = array_keys($cols);
|
||
|
$lines[] = "";
|
||
|
foreach ($cols as $col) {
|
||
|
$dcol = strtoupper($col)."_DCOL";
|
||
|
$lines[] = " const $dcol = [";
|
||
|
$lines[] = " \"name\" => \"$col\",";
|
||
|
$lines[] = " \"key\" => \"$col\",";
|
||
|
$lines[] = " ];";
|
||
|
}
|
||
|
$lines[] = " const DATA_SCHEMA = [";
|
||
|
foreach ($cols as $col) {
|
||
|
$dcol = strtoupper($col)."_DCOL";
|
||
|
$lines[] = " self::$dcol,";
|
||
|
}
|
||
|
$lines[] = " ];";
|
||
|
$lines[] = " const CSV_SCHEMA = self::DATA_SCHEMA;";
|
||
|
$lines[] = " const HTML_SCHEMA = self::DATA_SCHEMA;";
|
||
|
}
|
||
|
private static function add_schema(array &$lines, array $cols, string $indent): void {
|
||
|
$schema = [];
|
||
|
foreach ($cols as $col) {
|
||
|
$schema[$col["name"]] = $col["schema"];
|
||
|
}
|
||
|
md::normalize_mschema($schema);
|
||
|
$generator = new SrcGenerator($indent);
|
||
|
$generator->genConst("SCHEMA", $schema);
|
||
|
$generator->mergeInto($lines);
|
||
|
}
|
||
|
|
||
|
static function genclass($class, $options=null) {
|
||
|
$c = new ReflectionClass($class);
|
||
|
$force_dbtrait = $c->getConstant("DBTRAIT");
|
||
|
$default_dbtrait = $c->getConstant("DEFAULT_DBTRAIT");
|
||
|
|
||
|
self::reset($options);
|
||
|
$destdir = A::get($options, "destdir", ".");
|
||
|
$package = A::get($options, "package");
|
||
|
$baserow = A::get($options, "baserow");
|
||
|
$overwrite = A::get($options, "overwrite");
|
||
|
$verbose = A::get($options, "verbose");
|
||
|
if ($baserow === null) $baserow = AbstractRow::class;
|
||
|
|
||
|
$pos = strrpos($class, "\\");
|
||
|
$default_package = $pos !== false? substr($class, 0, $pos): false;
|
||
|
if ($package === null) $package = $default_package;
|
||
|
elseif (substr($package, 0, 1) != "\\") $package = "$default_package\\$package";
|
||
|
else $package = substr($package, 1);
|
||
|
|
||
|
# par défaut ne pas écraser les fichiers lors de la génération des classes
|
||
|
if ($overwrite === null) $overwrite = false;
|
||
|
# ne pas écraser les classes dont la définition est en +prod
|
||
|
$no_overwrites = [];
|
||
|
|
||
|
self::trace("# genclass");
|
||
|
self::trace(" class: $class");
|
||
|
self::trace(" destdir: $destdir");
|
||
|
|
||
|
$methods = self::get_methods($class);
|
||
|
foreach ($methods as $minfos) {
|
||
|
$update = func::call($minfos["method"]);
|
||
|
$dbtrait = $force_dbtrait;
|
||
|
if (!$dbtrait) $dbtrait = A::get($update, "+dbtrait");
|
||
|
if (!$dbtrait) $dbtrait = $default_dbtrait;
|
||
|
$prod = A::get($update, "+prod");
|
||
|
|
||
|
foreach ($update as $uname => $uinfos) {
|
||
|
# ignorer les directives de mise à jour: elles ont été traitées ci-dessus
|
||
|
if (str::starts_with("+", $uname)) {
|
||
|
if (!in_array($uname, self::UPDATE_DIRECTIVES)) {
|
||
|
trigger_error("$uname: unknown update directive", E_USER_WARNING);
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$type = A::get($uinfos, "+type", "table");
|
||
|
if ($type === "table") {
|
||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
$table = $uname;
|
||
|
$table_name = A::get($uinfos, "+name", $table);
|
||
|
$cols = $uinfos;
|
||
|
|
||
|
#######################################################################
|
||
|
# suppression de table
|
||
|
if ($cols === null) {
|
||
|
self::del_table($table);
|
||
|
if ($prod) unset($no_overwrites[$table]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# liste de champs "normale"
|
||
|
if (!self::has_table($table)) {
|
||
|
#####################################################################
|
||
|
# nouvelle table
|
||
|
$suffix = A::get($cols, "+suffix");
|
||
|
self::add_table($table, $suffix, $dbtrait, $table_name);
|
||
|
if ($prod) $no_overwrites[$table] = true;
|
||
|
foreach ($cols as $col => $value) {
|
||
|
if (!str::starts_with("+", $col)) {
|
||
|
$def = $value;
|
||
|
$cinfos = self::add_col($table, $col, $def);
|
||
|
if ($cinfos["pk"]) self::set_table_pk($table, $col);
|
||
|
} else {
|
||
|
switch ($col) {
|
||
|
case "+primary key":
|
||
|
$pk = A::with($value);
|
||
|
self::set_table_pk($table, $pk);
|
||
|
break;
|
||
|
case "+foreign key":
|
||
|
foreach (A::with($value) as $fk => $ref) {
|
||
|
$ms = null;
|
||
|
if (self::is_multi_fkdef($fk, $ms)) $fk = $ms[1];
|
||
|
if (self::is_single_fkref($ref, $ms)) {
|
||
|
self::add_t1link($fk, $table, $ms[2], $ms[1]);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (!in_array($col, self::TABLE_DIRECTIVES)) {
|
||
|
trigger_error("$table: $col: unknown table directive", E_USER_WARNING);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
#####################################################################
|
||
|
# maj d'une table existante
|
||
|
foreach ($cols as $col => $def) {
|
||
|
# les directives seront traitées plus tard
|
||
|
if (!str::starts_with("+", $col)) {
|
||
|
if ($def === null) {
|
||
|
## suppression d'un champ
|
||
|
if (!self::has_col($table, $col)) {
|
||
|
trigger_error("$table.$col: deleting a non-existent column", E_USER_WARNING);
|
||
|
}
|
||
|
self::del_col($table, $col);
|
||
|
} else {
|
||
|
if (!self::has_col($table, $col)) {
|
||
|
self::add_col($table, $col, $def);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
## maj directive
|
||
|
trigger_error("$table: $col: table directive update is not (yet) supported", E_USER_WARNING);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} elseif ($type === "view") {
|
||
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
$view = $uname;
|
||
|
$view_name = A::get($uinfos, "+name", $view);
|
||
|
$vinfos = $uinfos;
|
||
|
|
||
|
#####################################################################
|
||
|
# suppression de vue
|
||
|
if ($vinfos === null) {
|
||
|
self::del_view($view);
|
||
|
if ($prod) unset($no_overwrites[$view]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# définition "normale"
|
||
|
if (!self::has_view($view)) {
|
||
|
###################################################################
|
||
|
# nouvelle vue
|
||
|
$tables = A::get($vinfos, "+view_tables");
|
||
|
self::add_view($view, $tables, $dbtrait, $view_name);
|
||
|
if ($prod) $no_overwrites[$view] = true;
|
||
|
|
||
|
} else {
|
||
|
###################################################################
|
||
|
# maj d'une vue existante
|
||
|
trigger_error("$view: view update is not (yet) supported", E_USER_WARNING);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
trigger_error("$type: unknown type, expected table or view", E_USER_WARNING);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (self::$tables as $table => $tinfos) {
|
||
|
$class = str::upperw($table);
|
||
|
$class = preg_replace('/[^a-zA-Z0-9]*/', "", $class);
|
||
|
|
||
|
$suffix = $tinfos["suffix"];
|
||
|
$suffix = $suffix? "\"$suffix\"": "null";
|
||
|
|
||
|
$pk = $tinfos["pk"];
|
||
|
$pk_key = [];
|
||
|
if ($pk !== null) {
|
||
|
foreach ($pk as $key) {
|
||
|
$pk_key[] = "\"$key\"";
|
||
|
}
|
||
|
}
|
||
|
if (!$pk_key) {
|
||
|
$pk_key = "false";
|
||
|
} elseif (\count($pk_key) == 1) {
|
||
|
$pk_key = $pk_key[0];
|
||
|
} else {
|
||
|
$pk_key = "[".implode(", ", $pk_key)."]";
|
||
|
}
|
||
|
|
||
|
$pk_auto = false;
|
||
|
$cols = $tinfos["cols"];
|
||
|
foreach ($cols as $cinfos) {
|
||
|
if ($cinfos["auto"]) {
|
||
|
$pk_auto = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$pk_auto = $pk_auto? "true": "false";
|
||
|
|
||
|
$dbtrait = $tinfos["dbtrait"];
|
||
|
$table_name = $tinfos["name"];
|
||
|
|
||
|
###
|
||
|
|
||
|
$lines = ["<?php"];
|
||
|
if ($package) $lines[] = "namespace $package;";
|
||
|
$lines[] = "";
|
||
|
$lines[] = "class _$class extends \\$baserow {";
|
||
|
if ($dbtrait) {
|
||
|
$lines[] = " use \\$dbtrait;";
|
||
|
$lines[] = "";
|
||
|
}
|
||
|
$lines[] = " const TABLE_NAME = \"$table_name\";";
|
||
|
$lines[] = " const COL_SUFFIX = $suffix;";
|
||
|
$lines[] = " const PK_KEY = $pk_key;";
|
||
|
$lines[] = " const PK_AUTO = $pk_auto;";
|
||
|
$lines[] = " const COLUMNS = [";
|
||
|
foreach (array_keys($cols) as $col) {
|
||
|
$lines[] = " \"$col\",";
|
||
|
}
|
||
|
$lines[] = " ];";
|
||
|
|
||
|
#XXX désactiver pour le moment
|
||
|
#self::add_vtables($lines, $cols);
|
||
|
#self::add_data_schema($lines, $cols);
|
||
|
$lines[] = "}";
|
||
|
|
||
|
$overwrite_class = $overwrite || !A::has($no_overwrites, $table);
|
||
|
$outfname = "_$class.php";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite_class);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" php output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, $outf, $overwrite_class, true);
|
||
|
}
|
||
|
|
||
|
###
|
||
|
|
||
|
$lines = ["<?php"];
|
||
|
if ($package) $lines[] = "namespace $package;";
|
||
|
$lines[] = "";
|
||
|
$lines[] = "class $class extends _$class {";
|
||
|
$lines[] = "}";
|
||
|
|
||
|
# les fichiers Class ne sont jamais écrasés automatiquement
|
||
|
$overwrite_class = false; #$overwrite && !A::has($no_overwrites, $table);
|
||
|
$outfname = "$class.php";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite_class);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" php output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, $outf, $overwrite_class, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (self::$views as $view => $vinfos) {
|
||
|
$class = str::upperw($view);
|
||
|
$class = preg_replace('/[^a-zA-Z0-9]*/', "", $class);
|
||
|
|
||
|
$cols = [];
|
||
|
foreach ($vinfos["tables"] as $table) {
|
||
|
$cols = array_merge($cols, self::$tables[$table]["cols"]);
|
||
|
}
|
||
|
|
||
|
$dbtrait = $tinfos["dbtrait"];
|
||
|
$view_name = $vinfos["name"];
|
||
|
|
||
|
###
|
||
|
|
||
|
$lines = ["<?php"];
|
||
|
if ($package) $lines[] = "namespace $package;";
|
||
|
$lines[] = "";
|
||
|
$lines[] = "class _$class extends $baserow {";
|
||
|
if ($dbtrait) {
|
||
|
$lines[] = " use \\$dbtrait;";
|
||
|
$lines[] = "";
|
||
|
}
|
||
|
$lines[] = " const TABLE_NAME = \"$view_name\";";
|
||
|
$lines[] = " const COL_SUFFIX = null;";
|
||
|
$lines[] = " const PK_KEY = false;";
|
||
|
$lines[] = " const PK_AUTO = false;";
|
||
|
$lines[] = " const COLUMNS = [";
|
||
|
foreach (array_keys($cols) as $col) {
|
||
|
$lines[] = " \"$col\",";
|
||
|
}
|
||
|
$lines[] = " ];";
|
||
|
|
||
|
#XXX désactiver pour le moment
|
||
|
#self::add_vtables($lines, $cols);
|
||
|
#self::add_data_schema($lines, $cols);
|
||
|
$lines[] = "}";
|
||
|
|
||
|
$overwrite_class = $overwrite || !A::has($no_overwrites, $table);
|
||
|
$outfname = "_$class.php";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite_class);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" php output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, $outf, $overwrite_class, true);
|
||
|
}
|
||
|
|
||
|
###
|
||
|
|
||
|
$lines = ["<?php"];
|
||
|
if ($package) $lines[] = "namespace $package;";
|
||
|
$lines[] = "";
|
||
|
$lines[] = "class $class extends _$class {";
|
||
|
$lines[] = "}";
|
||
|
|
||
|
# les fichiers Class ne sont jamais écrasés automatiquement
|
||
|
$overwrite_class = false; #$overwrite && !A::has($no_overwrites, $view);
|
||
|
$outfname = "$class.php";
|
||
|
$outf = "$destdir/$outfname";
|
||
|
$ovsuf = self::get_ovsuf($outf, $overwrite_class);
|
||
|
if ($ovsuf || $verbose) {
|
||
|
self::trace(" php output: $outfname$ovsuf");
|
||
|
self::write_lines($lines, $outf, $overwrite_class, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|