Compare commits
14 Commits
982e313342
...
b20613f3b7
Author | SHA1 | Date | |
---|---|---|---|
b20613f3b7 | |||
12fe3a65a5 | |||
3e642b84bc | |||
599d646372 | |||
933b6cec3b | |||
4f17d19609 | |||
b71e879823 | |||
2c7020f44d | |||
f34694e12d | |||
5dc2d3d019 | |||
ce337ce3bc | |||
0945a86763 | |||
6bcd8d4cf6 | |||
70c82ae504 |
14
bin/._pman-composer_local_deps.php
Executable file
14
bin/._pman-composer_local_deps.php
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
require __DIR__ . "/../php/vendor/autoload.php";
|
||||
|
||||
use nulib\tools\pman\ComposerFile;
|
||||
use nulib\tools\pman\ComposerPmanFile;
|
||||
use nulib\ValueException;
|
||||
|
||||
$composer = new ComposerFile();
|
||||
|
||||
$deps = $composer->getLocalDeps();
|
||||
foreach ($deps as $dep => $path) {
|
||||
echo "$path\n";
|
||||
}
|
22
bin/._pman-composer_select_profile.php
Executable file
22
bin/._pman-composer_select_profile.php
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
require __DIR__ . "/../php/vendor/autoload.php";
|
||||
|
||||
use nulib\tools\pman\ComposerFile;
|
||||
use nulib\tools\pman\ComposerPmanFile;
|
||||
use nulib\ValueException;
|
||||
|
||||
$composer = new ComposerFile();
|
||||
$config = new ComposerPmanFile();
|
||||
|
||||
if ($argc <= 1) {
|
||||
throw new ValueException("Il faut spécifier le profil à sélectionner");
|
||||
}
|
||||
$profile = $argv[1];
|
||||
|
||||
$composer->selectProfile($profile, $config);
|
||||
if (getenv("PMAN_COMPOSER_DEBUG")) {
|
||||
$composer->print();
|
||||
} else {
|
||||
$composer->write();
|
||||
}
|
10
bin/_merge82
10
bin/_merge82
@ -1,4 +1,12 @@
|
||||
#!/bin/bash
|
||||
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
||||
source "$(dirname -- "$0")/../load.sh" || exit 1
|
||||
|
||||
exec "$(dirname -- "$0")/pmer" --tech-merge -Bdev82 dev74 -a "git checkout dev74" "$@"
|
||||
dev74=1
|
||||
args=(
|
||||
"merger la branche dev74 dans la branche dev82"
|
||||
-n,--no-dev74 dev74= "ne pas basculer sur la branche dev74 après la fusion"
|
||||
)
|
||||
parse_args "$@"; set -- "${args[@]}"
|
||||
|
||||
exec "$MYDIR/pmer" --tech-merge -Bdev82 dev74 ${dev74:+-a "git checkout dev74"} "$@"
|
||||
|
@ -1,14 +0,0 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
require __DIR__ . "/../php/vendor/autoload.php";
|
||||
|
||||
use nulib\tools\pman\ComposerFile;
|
||||
use nulib\tools\pman\ComposerPmanFile;
|
||||
use nulib\ValueException;
|
||||
|
||||
$composer = new ComposerFile();
|
||||
|
||||
$deps = $composer->getLocalDeps();
|
||||
foreach ($deps as $dep => $path) {
|
||||
echo "$path\n";
|
||||
}
|
1
bin/_pman-composer_local_deps.php
Symbolic link
1
bin/_pman-composer_local_deps.php
Symbolic link
@ -0,0 +1 @@
|
||||
runphp
|
@ -1,22 +0,0 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
require __DIR__ . "/../php/vendor/autoload.php";
|
||||
|
||||
use nulib\tools\pman\ComposerFile;
|
||||
use nulib\tools\pman\ComposerPmanFile;
|
||||
use nulib\ValueException;
|
||||
|
||||
$composer = new ComposerFile();
|
||||
$config = new ComposerPmanFile();
|
||||
|
||||
if ($argc <= 1) {
|
||||
throw new ValueException("Il faut spécifier le profil à sélectionner");
|
||||
}
|
||||
$profile = $argv[1];
|
||||
|
||||
$composer->selectProfile($profile, $config);
|
||||
if (getenv("PMAN_COMPOSER_DEBUG")) {
|
||||
$composer->print();
|
||||
} else {
|
||||
$composer->write();
|
||||
}
|
1
bin/_pman-composer_select_profile.php
Symbolic link
1
bin/_pman-composer_select_profile.php
Symbolic link
@ -0,0 +1 @@
|
||||
runphp
|
24
bin/jsondiff
Executable file
24
bin/jsondiff
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
||||
source "$(dirname -- "$0")/../load.sh" || exit 1
|
||||
|
||||
args=(
|
||||
"comparer deux fichiers JSON"
|
||||
"first.json second.json"
|
||||
)
|
||||
parse_args "$@"; set -- "${args[@]}"
|
||||
|
||||
first="$1"; shift
|
||||
[ -n "$first" ] || die "vous devez spécifier le premier fichier"
|
||||
second="$1"; shift
|
||||
[ -n "$second" ] || die "vous devez spécifier le deuxième fichier"
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
options=("$@")
|
||||
else
|
||||
options=(-u)
|
||||
fi
|
||||
|
||||
diff "${options[@]}" \
|
||||
<(jq . <"$first") \
|
||||
<(jq . <"$second")
|
21
bin/runphp
21
bin/runphp
@ -20,14 +20,25 @@ while true; do
|
||||
cd ..
|
||||
done
|
||||
|
||||
if [ -z "$PROJDIR" ]; then
|
||||
# s'il n'y a pas de projet, --bs est l'action par défaut
|
||||
[ $# -gt 0 ] || set -- --bs --ue
|
||||
elif [ "$MYNAME" == composer ]; then
|
||||
export RUNPHP_MOUNT=
|
||||
if [ "$MYNAME" == composer ]; then
|
||||
set -- composer "$@"
|
||||
elif [[ "$MYNAME" == *.php ]]; then
|
||||
# frontend pour une commande php
|
||||
set -- php "$MYDIR/.$MYNAME" "$@"
|
||||
# s'assurer que NULIBDIR est monté
|
||||
RUNPHP_MOUNT="$NULIBDIR"
|
||||
elif [ $# -eq 0 ]; then
|
||||
# s'il n'y a pas de projet, --bs est l'action par défaut
|
||||
[ -n "$PROJDIR" ] || set -- --bs --ue
|
||||
else
|
||||
case "$1" in
|
||||
*.php|*.phar) set -- php "$@";;
|
||||
*.php|*.phar)
|
||||
set -- php "$@"
|
||||
# s'assurer que le répertoire du script est monté
|
||||
setx RUNPHP_MOUNT=dirname "$1"
|
||||
setx RUNPHP_MOUNT=abspath "$RUNPHP_MOUNT"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
14
composer.lock
generated
14
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2d630ab5ff0ffe6139447cf93c362ed7",
|
||||
"content-hash": "4569957a35f86d8a4964d01c7358935c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@ -361,16 +361,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.4.0",
|
||||
"version": "v5.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "447a020a1f875a434d62f2a401f53b82a396e494"
|
||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
|
||||
"reference": "447a020a1f875a434d62f2a401f53b82a396e494",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
|
||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -413,9 +413,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
|
||||
},
|
||||
"time": "2024-12-30T11:07:19+00:00"
|
||||
"time": "2025-05-31T08:24:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nulib/tests",
|
||||
|
@ -143,6 +143,19 @@ class Capacitor implements ITransactor {
|
||||
return $this->storage->_charge($this->channel, $item, $func, $args, $row);
|
||||
}
|
||||
|
||||
function chargeAll(?iterable $items, $func=null, ?array $args=null): int {
|
||||
$count = 0;
|
||||
if ($items !== null) {
|
||||
if ($func !== null) {
|
||||
$func = func::with($func, $args)->bind($this->channel);
|
||||
}
|
||||
foreach ($items as $item) {
|
||||
$count += $this->charge($item, $func);
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
function discharge(bool $reset=true): Traversable {
|
||||
return $this->storage->_discharge($this->channel, $reset);
|
||||
}
|
||||
|
@ -214,8 +214,18 @@ class CapacitorChannel implements ITransactor {
|
||||
|
||||
protected ?array $migration;
|
||||
|
||||
function getMigration(): ?array {
|
||||
return $this->migration;
|
||||
function getMigration(?string $prefix=null): ?array {
|
||||
if ($prefix === null || $this->migration === null) return $this->migration;
|
||||
$migration = null;
|
||||
str::add_suffix($prefix, ":");
|
||||
foreach ($this->migration as $mkey => $mdef) {
|
||||
if (str::starts_with($prefix, $mkey)) {
|
||||
$migration[$mkey] = $mdef;
|
||||
} elseif (strpos($mkey, ":") === false) {
|
||||
$migration[$mkey] = $mdef;
|
||||
}
|
||||
}
|
||||
return $migration;
|
||||
}
|
||||
|
||||
protected ?array $primaryKeys;
|
||||
@ -444,27 +454,57 @@ class CapacitorChannel implements ITransactor {
|
||||
return $this->capacitor->charge($item, $func, $args, $row);
|
||||
}
|
||||
|
||||
function chargeAll(?iterable $items, $func=null, ?array $args=null): int {
|
||||
return $this->capacitor->chargeAll($items, $func, $args);
|
||||
}
|
||||
|
||||
function discharge(bool $reset=true): Traversable {
|
||||
return $this->capacitor->discharge($reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* retourner le filtre de base: les filtres de toutes les fonctions ci-dessous
|
||||
* sont fusionnées avec le filtre de base
|
||||
*
|
||||
* cela permet de limiter toutes les opérations à un sous-ensemble des données
|
||||
* du canal
|
||||
*/
|
||||
function getBaseFilter(): ?array {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function verifixFilter(&$filter): void {
|
||||
if ($filter !== null && !is_array($filter)) {
|
||||
$primaryKeys = $this->primaryKeys ?? ["id_"];
|
||||
$id = $filter;
|
||||
$this->verifixId($id);
|
||||
$filter = [$primaryKeys[0] => $id];
|
||||
}
|
||||
$filter = cl::merge($this->getBaseFilter(), $filter);
|
||||
}
|
||||
|
||||
function count($filter=null): int {
|
||||
$this->verifixFilter($filter);
|
||||
return $this->capacitor->count($filter);
|
||||
}
|
||||
|
||||
function one($filter, ?array $mergeQuery=null): ?array {
|
||||
$this->verifixFilter($filter);
|
||||
return $this->capacitor->one($filter, $mergeQuery);
|
||||
}
|
||||
|
||||
function all($filter, ?array $mergeQuery=null): Traversable {
|
||||
$this->verifixFilter($filter);
|
||||
return $this->capacitor->all($filter, $mergeQuery);
|
||||
}
|
||||
|
||||
function each($filter, $func=null, ?array $args=null, ?array $mergeQuery=null, ?int &$nbUpdated=null): int {
|
||||
$this->verifixFilter($filter);
|
||||
return $this->capacitor->each($filter, $func, $args, $mergeQuery, $nbUpdated);
|
||||
}
|
||||
|
||||
function delete($filter, $func=null, ?array $args=null): int {
|
||||
$this->verifixFilter($filter);
|
||||
return $this->capacitor->delete($filter, $func, $args);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ abstract class CapacitorStorage {
|
||||
const GENLIC_DEFINITION = "varchar(80)";
|
||||
const GENLIB_DEFINITION = "varchar(255)";
|
||||
const GENTEXT_DEFINITION = "mediumtext";
|
||||
const GENBOOL_DEFINITION = "integer(1)";
|
||||
const GENBOOL_DEFINITION = "integer(1) default 0";
|
||||
const GENUUID_DEFINITION = "varchar(36)";
|
||||
|
||||
protected static function gencol($def): string {
|
||||
@ -122,7 +122,7 @@ abstract class CapacitorStorage {
|
||||
}
|
||||
|
||||
protected function getMigration(CapacitorChannel $channel): ?array {
|
||||
return $channel->getMigration();
|
||||
return $channel->getMigration($this->db()->getPrefix());
|
||||
}
|
||||
|
||||
/** sérialiser les valeurs qui doivent l'être dans $row */
|
||||
@ -434,7 +434,7 @@ abstract class CapacitorStorage {
|
||||
}
|
||||
|
||||
if ($func !== null) {
|
||||
$updates = func::with($func)
|
||||
$updates = func::with($func, $args)
|
||||
->prependArgs([$item, $row, $prow])
|
||||
->bind($channel)
|
||||
->invoke();
|
||||
|
@ -2,6 +2,12 @@
|
||||
namespace nulib\db;
|
||||
|
||||
interface IDatabase extends ITransactor {
|
||||
/**
|
||||
* retourner le type de la base de données (mysql, pgsql, sqlite, ...)
|
||||
* ce préfixe peut servir à qualifier les migrations
|
||||
*/
|
||||
function getPrefix(): ?string;
|
||||
|
||||
/** obtenir la requête SQL correspondant à $query */
|
||||
function getSql($query, ?array $params=null): string;
|
||||
|
||||
|
@ -4,6 +4,8 @@ namespace nulib\db\mysql;
|
||||
use nulib\db\pdo\Pdo;
|
||||
|
||||
class Mysql extends Pdo {
|
||||
const PREFIX = "mysql";
|
||||
|
||||
function getDbname(): ?string {
|
||||
$url = $this->dbconn["name"] ?? null;
|
||||
if ($url !== null && preg_match('/^mysql(?::|.*;)dbname=([^;]+)/i', $url, $ms)) {
|
||||
|
@ -39,7 +39,7 @@ class MysqlStorage extends CapacitorStorage {
|
||||
function _getMigration(CapacitorChannel $channel): _mysqlMigration {
|
||||
$migrations = cl::merge([
|
||||
"0init" => [$this->_createSql($channel)],
|
||||
], $channel->getMigration());
|
||||
], $channel->getMigration($this->db->getPrefix()));
|
||||
return new _mysqlMigration($migrations, $channel->getName());
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,12 @@ use nulib\ValueException;
|
||||
class Pdo implements IDatabase {
|
||||
use Tvalues;
|
||||
|
||||
const PREFIX = null;
|
||||
|
||||
function getPrefix(): ?string {
|
||||
return static::PREFIX;
|
||||
}
|
||||
|
||||
static function with($pdo, ?array $params=null): self {
|
||||
if ($pdo instanceof static) {
|
||||
return $pdo;
|
||||
|
@ -12,6 +12,12 @@ use nulib\ValueException;
|
||||
class Pgsql implements IDatabase {
|
||||
use Tvalues;
|
||||
|
||||
const PREFIX = "pgsql";
|
||||
|
||||
function getPrefix(): ?string {
|
||||
return self::PREFIX;
|
||||
}
|
||||
|
||||
static function with($pgsql, ?array $params=null): self {
|
||||
if ($pgsql instanceof static) {
|
||||
return $pgsql;
|
||||
|
@ -11,7 +11,7 @@ class PgsqlStorage extends CapacitorStorage {
|
||||
const SERTS_DEFINITION = "timestamp";
|
||||
const GENSERIAL_DEFINITION = "serial primary key";
|
||||
const GENTEXT_DEFINITION = "text";
|
||||
const GENBOOL_DEFINITION = "boolean";
|
||||
const GENBOOL_DEFINITION = "boolean default false";
|
||||
const GENUUID_DEFINITION = "uuid";
|
||||
|
||||
function __construct($pgsql) {
|
||||
@ -44,7 +44,7 @@ class PgsqlStorage extends CapacitorStorage {
|
||||
function _getMigration(CapacitorChannel $channel): _pgsqlMigration {
|
||||
$migrations = cl::merge([
|
||||
"0init" => [$this->_createSql($channel)],
|
||||
], $channel->getMigration());
|
||||
], $channel->getMigration($this->db->getPrefix()));
|
||||
return new _pgsqlMigration($migrations, $channel->getName());
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,12 @@ use SQLite3Stmt;
|
||||
class Sqlite implements IDatabase {
|
||||
use Tvalues;
|
||||
|
||||
const PREFIX = "sqlite";
|
||||
|
||||
function getPrefix(): ?string {
|
||||
return self::PREFIX;
|
||||
}
|
||||
|
||||
static function with($sqlite, ?array $params=null): self {
|
||||
if ($sqlite instanceof static) {
|
||||
return $sqlite;
|
||||
|
@ -34,7 +34,7 @@ class SqliteStorage extends CapacitorStorage {
|
||||
function _getMigration(CapacitorChannel $channel): _sqliteMigration {
|
||||
$migrations = cl::merge([
|
||||
"0init" => [$this->_createSql($channel)],
|
||||
], $channel->getMigration());
|
||||
], $channel->getMigration($this->db->getPrefix()));
|
||||
return new _sqliteMigration($migrations, $channel->getName());
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ class CsvBuilder extends AbstractBuilder {
|
||||
}
|
||||
|
||||
protected function _checkOk(): bool {
|
||||
$size = $this->ftell();
|
||||
$this->size = $size = $this->ftell();
|
||||
if ($size === 0) return false;
|
||||
$this->rewind();
|
||||
return true;
|
||||
|
@ -118,18 +118,8 @@ abstract class AbstractBuilder extends TempStream implements IBuilder {
|
||||
if ($unsetRows) $this->rows = null;
|
||||
}
|
||||
|
||||
abstract protected function _sendContentType(): void;
|
||||
|
||||
protected bool $sentHeaders = false;
|
||||
|
||||
function sendHeaders(): void {
|
||||
if ($this->sentHeaders) return;
|
||||
$this->_sendContentType();
|
||||
$output = $this->output;
|
||||
if ($output !== null) {
|
||||
http::download_as(path::filename($output));
|
||||
}
|
||||
$this->sentHeaders = true;
|
||||
function getCount(): int {
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
protected function _build(?iterable $rows=null): void {
|
||||
@ -141,6 +131,8 @@ abstract class AbstractBuilder extends TempStream implements IBuilder {
|
||||
|
||||
protected bool $built = false, $closed = false;
|
||||
|
||||
protected ?int $size = null;
|
||||
|
||||
function build(?iterable $rows=null, bool $close=true): bool {
|
||||
$ok = true;
|
||||
if (!$this->built) {
|
||||
@ -154,6 +146,24 @@ abstract class AbstractBuilder extends TempStream implements IBuilder {
|
||||
return $ok;
|
||||
}
|
||||
|
||||
abstract protected function _sendContentType(): void;
|
||||
|
||||
protected bool $sentHeaders = false;
|
||||
|
||||
function sendHeaders(): void {
|
||||
if ($this->sentHeaders) return;
|
||||
$this->_sendContentType();
|
||||
$output = $this->output;
|
||||
if ($output !== null) {
|
||||
http::download_as(path::filename($output));
|
||||
}
|
||||
$size = $this->size;
|
||||
if ($size !== null) {
|
||||
header("Content-Length: $size");
|
||||
}
|
||||
$this->sentHeaders = true;
|
||||
}
|
||||
|
||||
function sendFile(?iterable $rows=null): int {
|
||||
if (!$this->built) {
|
||||
$this->_build($rows);
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace nulib\file\tab;
|
||||
|
||||
use nulib\file\IWriter;
|
||||
|
||||
interface IBuilder extends \nulib\file\IReader {
|
||||
function writeHeaders(?array $headers=null): void;
|
||||
|
||||
@ -8,7 +10,23 @@ interface IBuilder extends \nulib\file\IReader {
|
||||
|
||||
function writeAll(?iterable $rows=null): void;
|
||||
|
||||
/**
|
||||
* @return int le nombre de ligne qui ont été écrites (sans compte l'en-tête)
|
||||
*/
|
||||
function getCount(): int;
|
||||
|
||||
/** préparer le fichier (sans l'envoyer) */
|
||||
function build(?iterable $rows=null, bool $close=true): bool;
|
||||
|
||||
/** enregistrer le fichier préparé avec {@link build()} */
|
||||
function copyTo(IWriter $dest, bool $closeWriter=false, bool $closeReader=true): void;
|
||||
|
||||
/** envoyer les en-têtes */
|
||||
function sendHeaders(): void;
|
||||
|
||||
/**
|
||||
* envoyer le fichier pour téléchargement (la méthode {@link sendHeaders()}
|
||||
* est automatiquement appelée le cas échéant
|
||||
*/
|
||||
function sendFile(?iterable $rows=null): int;
|
||||
}
|
||||
|
@ -15,6 +15,6 @@ class Date extends DateTime {
|
||||
}
|
||||
|
||||
function format($format=self::DEFAULT_FORMAT): string {
|
||||
return \DateTime::format($format);
|
||||
return parent::format($format);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace nulib\php\time;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use InvalidArgumentException;
|
||||
use nulib\str;
|
||||
|
||||
/**
|
||||
* Class DateTime: une date et une heure
|
||||
@ -29,10 +30,20 @@ class DateTime extends \DateTime {
|
||||
else return new static($datetime);
|
||||
}
|
||||
|
||||
static function withn($datetime): ?self {
|
||||
if ($datetime === null) return null;
|
||||
elseif ($datetime instanceof static) return $datetime;
|
||||
else return new static($datetime);
|
||||
}
|
||||
|
||||
static function ensure(&$datetime): void {
|
||||
$datetime = static::withn($datetime);
|
||||
}
|
||||
|
||||
const DMY_PATTERN = '/^(\d+)\/(\d+)(?:\/(\d+))?$/';
|
||||
const YMD_PATTERN = '/^((?:\d{2})?\d{2})(\d{2})(\d{2})$/';
|
||||
const DMYHIS_PATTERN = '/^(\d+)\/(\d+)(?:\/(\d+))? +(\d+)[h:.](\d+)(?:[:.](\d+))?$/';
|
||||
const YMDHISZ_PATTERN = '/^((?:\d{2})?\d{2})(\d{2})(\d{2})[tT](\d{2})(\d{2})(\d{2})?([zZ])?$/';
|
||||
const YMDHISZ_PATTERN = '/^((?:\d{2})?\d{2})-?(\d{2})-?(\d{2})[tT](\d{2}):?(\d{2}):?(\d{2})?([zZ]|\+\d{2}:?\d{2})?$/';
|
||||
|
||||
|
||||
protected static function get_value(array $datetime, ?string $key, ?string $k, ?int $index) {
|
||||
@ -206,16 +217,34 @@ class DateTime extends \DateTime {
|
||||
return $year;
|
||||
}
|
||||
|
||||
function __construct($datetime="now", DateTimeZone $timezone=null) {
|
||||
static function fix_z(?string $Z): ?string {
|
||||
$Z = strtoupper($Z);
|
||||
str::del_prefix($Z, "+");
|
||||
if (preg_match('/^\d{4}$/', $Z)) {
|
||||
$Z = substr($Z, 0, 2).":".substr($Z, 2);
|
||||
}
|
||||
if ($Z === "Z" || $Z === "UTC" || $Z === "00:00") return "UTC";
|
||||
return "GMT+$Z";
|
||||
}
|
||||
|
||||
function __construct($datetime="now", DateTimeZone $timezone=null, ?bool $forceLocalTimezone=null) {
|
||||
$forceLocalTimezone ??= $timezone === null;
|
||||
if ($forceLocalTimezone) {
|
||||
$setTimezone = $timezone;
|
||||
$timezone = null;
|
||||
}
|
||||
|
||||
$datetime ??= "now";
|
||||
if ($datetime instanceof \DateTimeInterface) {
|
||||
if ($timezone === null) $timezone = $datetime->getTimezone();
|
||||
$timezone ??= $datetime->getTimezone();
|
||||
parent::__construct();
|
||||
$this->setTimestamp($datetime->getTimestamp());
|
||||
$this->setTimezone($timezone);
|
||||
|
||||
} elseif (is_int($datetime)) {
|
||||
parent::__construct("now", $timezone);
|
||||
$this->setTimestamp($datetime);
|
||||
|
||||
} elseif (is_string($datetime)) {
|
||||
$Y = $H = $Z = null;
|
||||
if (preg_match(self::DMY_PATTERN, $datetime, $ms)) {
|
||||
@ -253,22 +282,30 @@ class DateTime extends \DateTime {
|
||||
if ($Y !== null) {
|
||||
if ($H === null) $datetime = sprintf("%04d-%02d-%02d", $Y, $m, $d);
|
||||
else $datetime = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $Y, $m, $d, $H, $M, $S);
|
||||
if ($Z !== null) $timezone = new DateTimeZone("UTC");
|
||||
if ($Z !== null) $timezone = new DateTimeZone(self::fix_z($Z));
|
||||
}
|
||||
parent::__construct($datetime, $timezone);
|
||||
|
||||
} elseif (is_array($datetime) && ($datetime = self::parse_array($datetime)) !== null) {
|
||||
[$Y, $m, $d, $H, $M, $S, $tz] = $datetime;
|
||||
$setTimezone = $timezone;
|
||||
$timezone = null;
|
||||
[$Y, $m, $d, $H, $M, $S, $Z] = $datetime;
|
||||
if ($H === null && $M === null && $S === null) {
|
||||
$datetime = sprintf("%04d-%02d-%02d", $Y, $m, $d);
|
||||
} else {
|
||||
$datetime = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $Y, $m, $d, $H ?? 0, $M ?? 0, $S ?? 0);
|
||||
}
|
||||
if ($tz === "Z" || $tz === "z") $tz = "UTC";
|
||||
$timezone = $tz !== null? new DateTimeZone($tz): null;
|
||||
if ($Z !== null) $timezone = new DateTimeZone(self::fix_z($Z));
|
||||
parent::__construct($datetime, $timezone);
|
||||
|
||||
} else {
|
||||
throw new InvalidArgumentException("datetime must be a string or an instance of DateTimeInterface");
|
||||
}
|
||||
|
||||
if ($forceLocalTimezone) {
|
||||
$setTimezone ??= new DateTimeZone(date_default_timezone_get());
|
||||
$this->setTimezone($setTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
function diff($target, $absolute=false): DateInterval {
|
||||
@ -276,7 +313,13 @@ class DateTime extends \DateTime {
|
||||
}
|
||||
|
||||
function format($format=self::DEFAULT_FORMAT): string {
|
||||
return \DateTime::format($format);
|
||||
if (array_key_exists($format, self::INT_FORMATS)) {
|
||||
$format = self::INT_FORMATS[$format];
|
||||
} elseif (array_key_exists($format, self::STRING_FORMATS)) {
|
||||
$format = self::STRING_FORMATS[$format];
|
||||
}
|
||||
if (is_callable($format)) return $format($this);
|
||||
else return \DateTime::format($format);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,7 @@ use nulib\db\Capacitor;
|
||||
use nulib\db\sqlite\impl\MyChannel;
|
||||
use nulib\db\sqlite\impl\MyChannelV2;
|
||||
use nulib\db\sqlite\impl\MyChannelV3;
|
||||
use nulib\db\sqlite\impl\MyIndexChannel;
|
||||
use nulib\output\msg;
|
||||
use nulib\output\std\StdMessenger;
|
||||
use nulib\php\time\DateTime;
|
||||
@ -69,6 +70,42 @@ alter table my add column date_mod datetime;
|
||||
-- infos
|
||||
alter table my add column age integer;
|
||||
|
||||
EOT;
|
||||
self::assertSame($expected, $sql);
|
||||
}
|
||||
|
||||
function testMigrationIndex() {
|
||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
||||
$data = [
|
||||
["un", "premier", "first"],
|
||||
["deux", "deuxieme", "second"],
|
||||
];
|
||||
|
||||
new Capacitor($storage, $channel = new MyIndexChannel());
|
||||
$channel->reset(true);
|
||||
$channel->chargeAll($data);
|
||||
|
||||
$sql = $channel->getCapacitor()->getCreateSql();
|
||||
$class = MyIndexChannel::class;
|
||||
$expected = <<<EOT
|
||||
-- -*- coding: utf-8 mode: sql -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
||||
-- autogénéré à partir de $class
|
||||
|
||||
-- 0init
|
||||
create table if not exists my_index (
|
||||
name varchar not null primary key
|
||||
, first varchar
|
||||
, second varchar
|
||||
, item__ mediumtext
|
||||
, item__sum_ varchar(40)
|
||||
, created_ datetime
|
||||
, modified_ datetime
|
||||
);
|
||||
|
||||
-- index
|
||||
create index my_index_first on my_index(first);
|
||||
create index my_index_second on my_index(second);
|
||||
|
||||
EOT;
|
||||
self::assertSame($expected, $sql);
|
||||
}
|
||||
|
29
php/tests/db/sqlite/impl/MyIndexChannel.php
Normal file
29
php/tests/db/sqlite/impl/MyIndexChannel.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace nulib\db\sqlite\impl;
|
||||
|
||||
use nulib\cl;
|
||||
use nulib\db\CapacitorChannel;
|
||||
|
||||
class MyIndexChannel extends CapacitorChannel {
|
||||
const NAME = "my_index";
|
||||
const TABLE_NAME = "my_index";
|
||||
const COLUMN_DEFINITIONS = [
|
||||
"name" => "varchar not null primary key",
|
||||
"first" => "varchar",
|
||||
"second" => "varchar",
|
||||
];
|
||||
const MIGRATION = [
|
||||
"index" => [
|
||||
"create index my_index_first on my_index(first)",
|
||||
"create index my_index_second on my_index(second)",
|
||||
],
|
||||
];
|
||||
|
||||
function getItemValues($item): ?array {
|
||||
return cl::select($item, [
|
||||
"name" => 0,
|
||||
"first" => 1,
|
||||
"second" => 2,
|
||||
]);
|
||||
}
|
||||
}
|
@ -467,9 +467,11 @@ OPTIONS
|
||||
|
||||
function host_docker_run() {
|
||||
# lancer une commande avec docker
|
||||
if [ "$1" == composer ]; then
|
||||
: # pas d'analyse d'argument pour composer
|
||||
else
|
||||
case "$1" in
|
||||
ci|cu|composer)
|
||||
: # pas d'analyse d'argument pour les commandes composer
|
||||
;;
|
||||
*)
|
||||
SOPTS=+w:
|
||||
LOPTS=help,chdir:,no-use-rslave
|
||||
args="$(getopt -n "$MYNAME" -o "$SOPTS" -l "$LOPTS" -- "$@")" || exit 1; eval "set -- $args"
|
||||
@ -513,7 +515,7 @@ OPTIONS
|
||||
esac
|
||||
shift
|
||||
done
|
||||
fi
|
||||
esac
|
||||
|
||||
args=(
|
||||
run -it --rm
|
||||
@ -535,26 +537,34 @@ OPTIONS
|
||||
|
||||
# monter le répertoire qui contient $PROJDIR
|
||||
mount_composer=
|
||||
mount_runphp=1
|
||||
mount_standalone=1
|
||||
mount_mount=1
|
||||
if [ -z "$PROJDIR" -o "${PROJDIR#$HOME/}" != "$PROJDIR" -o "$PROJDIR" == "$HOME" ]; then
|
||||
# bind mount $HOME
|
||||
args+=(-v "$HOME:$HOME${UseRslave:+:rslave}")
|
||||
[ -n "$RUNPHP_STANDALONE" ] &&
|
||||
[ "${RUNPHP_STANDALONE#$HOME/}" != "$RUNPHP_STANDALONE" ] &&
|
||||
mount_runphp=
|
||||
if [ -n "$RUNPHP_STANDALONE" -a "${RUNPHP_STANDALONE#$HOME/}" != "$RUNPHP_STANDALONE" ]; then
|
||||
mount_standalone=
|
||||
fi
|
||||
if [ -n "$RUNPHP_MOUNT" -a "${RUNPHP_MOUNT#$HOME/}" != "$RUNPHP_MOUNT" ]; then
|
||||
mount_mount=
|
||||
fi
|
||||
elif [ -n "$PROJDIR" ]; then
|
||||
# bind mount uniquement le répertoire du projet
|
||||
args+=(-v "$PROJDIR:$PROJDIR${UseRslave:+:rslave}")
|
||||
mount_composer=1
|
||||
[ "$RUNPHP_STANDALONE" == "$PROJDIR" ] && mount_runphp=
|
||||
[ "$RUNPHP_STANDALONE" == "$PROJDIR" ] && mount_standalone=
|
||||
[ "$RUNPHP_MOUNT" == "$PROJDIR" ] && mount_mount=
|
||||
fi
|
||||
if [ -n "$mount_composer" -a -d "$HOME/.composer" ]; then
|
||||
# monter la configuration de composer
|
||||
args+=(-v "$HOME/.composer:$HOME/.composer")
|
||||
fi
|
||||
if [ -n "$RUNPHP_STANDALONE" -a -n "$mount_runphp" ]; then
|
||||
if [ -n "$RUNPHP_STANDALONE" -a -n "$mount_standalone" ]; then
|
||||
args+=(-v "$RUNPHP_STANDALONE:$RUNPHP_STANDALONE")
|
||||
fi
|
||||
if [ -n "$RUNPHP_MOUNT" -a -n "$mount_mount" ]; then
|
||||
args+=(-v "$RUNPHP_MOUNT:$RUNPHP_MOUNT")
|
||||
fi
|
||||
args+=(-w "$(pwd)")
|
||||
|
||||
# lancer avec l'utilisateur courant
|
||||
@ -589,9 +599,22 @@ function container_exec() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" == composer ]; then
|
||||
: # pas d'analyse d'argument pour composer
|
||||
else
|
||||
# pour les commandes suivantes, pas d'analyse d'argument
|
||||
case "$1" in
|
||||
ci)
|
||||
eecho "== installing composer dependencies"
|
||||
shift
|
||||
composer i "$@"
|
||||
;;
|
||||
cu)
|
||||
eecho "== upgrading composer dependencies"
|
||||
shift
|
||||
composer u "$@"
|
||||
;;
|
||||
composer)
|
||||
"$@"
|
||||
;;
|
||||
*)
|
||||
SOPTS=+w:
|
||||
LOPTS=chdir:
|
||||
args="$(getopt -n "$MYNAME" -o "$SOPTS" -l "$LOPTS" -- "$@")" || exit 1; eval "set -- $args"
|
||||
@ -606,26 +629,14 @@ function container_exec() {
|
||||
esac
|
||||
shift
|
||||
done
|
||||
fi
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
die "no command specified"
|
||||
elif [ "$1" == ci ]; then
|
||||
eecho "== installing composer dependencies"
|
||||
shift
|
||||
composer i "$@"
|
||||
elif [ "$1" == cu ]; then
|
||||
eecho "== upgrading composer dependencies"
|
||||
shift
|
||||
composer u "$@"
|
||||
elif [ "$1" == composer ]; then
|
||||
"$@"
|
||||
else
|
||||
[ $# -gt 0 ] || die "no command specified"
|
||||
if [ -n "$chdir" ]; then
|
||||
cd "$chdir" || exit 1
|
||||
fi
|
||||
exec "$@"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
Loading…
x
Reference in New Issue
Block a user