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 = [
 | 
						|
      "",
 | 
						|
      "~~~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 = [
 | 
						|
      "",
 | 
						|
      "~~~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);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |