Compare commits
74 Commits
Author | SHA1 | Date | |
---|---|---|---|
a045296629 | |||
68f5dc5209 | |||
f005692cd8 | |||
d844bd03ad | |||
c4e02d5bcf | |||
2a92a9a07e | |||
c274adb6e6 | |||
ff02ffdf4f | |||
9109e0fe39 | |||
fd1ebaf611 | |||
26817d2826 | |||
7257654753 | |||
e2bec38540 | |||
c2ec23be30 | |||
741a807420 | |||
e9f4826a94 | |||
fd9b3b29bc | |||
f37a53cfbd | |||
6e8245dbcb | |||
f4e252a6e0 | |||
19f6f9c9e1 | |||
baa770d969 | |||
c5bfa3940c | |||
39af99ffa4 | |||
1fc4e7637b | |||
ef7e8551aa | |||
9328aac9e9 | |||
62fc315b9e | |||
b9e91bd917 | |||
3608b02749 | |||
d148850c12 | |||
189c7aba68 | |||
91e6c0dcd2 | |||
bb311708d7 | |||
1feffb6c0f | |||
62987d576e | |||
5decd631e2 | |||
c98afaa98c | |||
3994855c7e | |||
6ba0a16e84 | |||
56fda96c78 | |||
d6078e8b52 | |||
69203352d8 | |||
26b483a29f | |||
6e93c7da62 | |||
66397a3bd0 | |||
eec9215431 | |||
1340c90d62 | |||
07a6d91385 | |||
41bcb07367 | |||
1ad63a92a9 | |||
cb1918e1fd | |||
8a7b60e2d3 | |||
50cf0eca33 | |||
4b84f11f99 | |||
9cf0e8045a | |||
08d5327afa | |||
4335a63d76 | |||
7227cd7bdd | |||
bb5b30480e | |||
c5c4119e69 | |||
089e4872aa | |||
fc523bfb6c | |||
e1b4ef4cc3 | |||
a705dd61c4 | |||
f8eec57055 | |||
74487a0ab9 | |||
68023b72ee | |||
1aa266b509 | |||
1685a40906 | |||
998d353758 | |||
1a5ca79ef7 | |||
2812046b4b | |||
9438aaf396 |
16
.composer.pman.yml
Normal file
16
.composer.pman.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# -*- coding: utf-8 mode: yaml -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
|
||||||
|
|
||||||
|
composer:
|
||||||
|
profiles: [ dev, dist ]
|
||||||
|
dev:
|
||||||
|
link: true
|
||||||
|
require-dev:
|
||||||
|
nulib/php: ^7.4-dev
|
||||||
|
nulib/spout: ^7.4-dev
|
||||||
|
nulib/phpss: ^7.4-dev
|
||||||
|
dist:
|
||||||
|
link: false
|
||||||
|
require-dev:
|
||||||
|
nulib/php: ^0.4.0p74
|
||||||
|
nulib/spout: ^0.4.0p74
|
||||||
|
nulib/phpss: ^0.4.0p74
|
7
.idea/inspectionProfiles/Project_Default.xml
generated
7
.idea/inspectionProfiles/Project_Default.xml
generated
@ -2,5 +2,12 @@
|
|||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
|
<option name="processCode" value="true" />
|
||||||
|
<option name="processLiterals" value="true" />
|
||||||
|
<option name="processComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
6
.idea/nur-ture.iml
generated
6
.idea/nur-ture.iml
generated
@ -4,10 +4,8 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/nur_src" isTestSource="false" packagePrefix="nur\" />
|
<sourceFolder url="file://$MODULE_DIR$/nur_src" isTestSource="false" packagePrefix="nur\" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/nur_tests" isTestSource="true" packagePrefix="nur\" />
|
<sourceFolder url="file://$MODULE_DIR$/nur_tests" isTestSource="true" packagePrefix="nur\" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nur\sery\wip\" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nulib\" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src_app" isTestSource="false" packagePrefix="nur\sery\" />
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nulib\" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nur\sery\" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src_glue" isTestSource="false" packagePrefix="nulib\" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
15
.idea/php.xml
generated
15
.idea/php.xml
generated
@ -10,6 +10,11 @@
|
|||||||
<option name="highlightLevel" value="WARNING" />
|
<option name="highlightLevel" value="WARNING" />
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PhpCodeSniffer">
|
||||||
|
<phpcs_settings>
|
||||||
|
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="30000" />
|
||||||
|
</phpcs_settings>
|
||||||
|
</component>
|
||||||
<component name="PhpIncludePathManager">
|
<component name="PhpIncludePathManager">
|
||||||
<include_path>
|
<include_path>
|
||||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
||||||
@ -64,6 +69,11 @@
|
|||||||
</include_path>
|
</include_path>
|
||||||
</component>
|
</component>
|
||||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.4" />
|
<component name="PhpProjectSharedConfiguration" php_language_level="7.4" />
|
||||||
|
<component name="PhpStan">
|
||||||
|
<PhpStan_settings>
|
||||||
|
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="60000" />
|
||||||
|
</PhpStan_settings>
|
||||||
|
</component>
|
||||||
<component name="PhpStanOptionsConfiguration">
|
<component name="PhpStanOptionsConfiguration">
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
@ -72,6 +82,11 @@
|
|||||||
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
|
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" />
|
||||||
</phpunit_settings>
|
</phpunit_settings>
|
||||||
</component>
|
</component>
|
||||||
|
<component name="Psalm">
|
||||||
|
<Psalm_settings>
|
||||||
|
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="846389f7-9fb5-4173-a868-1dc6b8fbb3fa" timeout="60000" />
|
||||||
|
</Psalm_settings>
|
||||||
|
</component>
|
||||||
<component name="PsalmOptionsConfiguration">
|
<component name="PsalmOptionsConfiguration">
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
17
.pman.conf
17
.pman.conf
@ -9,4 +9,19 @@ TAG_PREFIX=
|
|||||||
TAG_SUFFIX=p74
|
TAG_SUFFIX=p74
|
||||||
HOTFIX=hotf74-
|
HOTFIX=hotf74-
|
||||||
DIST=
|
DIST=
|
||||||
NOAUTO=1
|
NOAUTO=
|
||||||
|
|
||||||
|
AFTER_CREATE_RELEASE='
|
||||||
|
set -x
|
||||||
|
pman --composer-select-profile dist
|
||||||
|
composer u || exit 1
|
||||||
|
git commit -am "<pman>deps de dist"
|
||||||
|
true
|
||||||
|
'
|
||||||
|
AFTER_MERGE_RELEASE='
|
||||||
|
set -x
|
||||||
|
pman --composer-select-profile dev
|
||||||
|
composer u || exit 1
|
||||||
|
git commit -am "<pman>deps de dev"
|
||||||
|
true
|
||||||
|
'
|
||||||
|
21
CHANGES.md
21
CHANGES.md
@ -1,3 +1,24 @@
|
|||||||
|
## Release 0.4.1p82 du 17/03/2025-17:23
|
||||||
|
|
||||||
|
## Release 0.4.1p74 du 17/03/2025-17:19
|
||||||
|
|
||||||
|
* `56fda96` changer le type de variables gérées par EnvConfig
|
||||||
|
|
||||||
|
## Release 0.4.0p82 du 14/03/2025-15:46
|
||||||
|
|
||||||
|
## Release 0.4.0p74 du 14/03/2025-15:44
|
||||||
|
|
||||||
|
* `4b84f11` début assocSchema
|
||||||
|
* `7227cd7` maj TODO
|
||||||
|
* `089e487` renommer dest en value; tenir compte de la valeur par défaut
|
||||||
|
* `fc523bf` instancier type le plus vite possible
|
||||||
|
* `f8eec57` renommer Value en Wrapper
|
||||||
|
* `1aa266b` tstring et trawstring
|
||||||
|
* `1a5ca79` support des colonnes
|
||||||
|
* `9438aaf` ajout config .pman.yml
|
||||||
|
|
||||||
|
## Release 0.3.0p82 du 01/03/2025-13:49
|
||||||
|
|
||||||
## Release 0.3.0p74 du 01/03/2025-13:44
|
## Release 0.3.0p74 du 01/03/2025-13:44
|
||||||
|
|
||||||
release initiale
|
release initiale
|
||||||
|
@ -1 +1 @@
|
|||||||
0.3.0
|
0.4.1
|
||||||
|
@ -64,9 +64,7 @@
|
|||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"nulib\\": "src_glue",
|
"nulib\\": "src",
|
||||||
"nur\\sery\\wip\\": "src",
|
|
||||||
"nur\\sery\\": "src_app",
|
|
||||||
"nur\\": "nur_src"
|
"nur\\": "nur_src"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -75,7 +73,7 @@
|
|||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"nur\\sery\\": "tests",
|
"nulib\\": "tests",
|
||||||
"nur\\": "nur_tests"
|
"nur\\": "nur_tests"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
8
composer.lock
generated
8
composer.lock
generated
@ -589,7 +589,7 @@
|
|||||||
"dist": {
|
"dist": {
|
||||||
"type": "path",
|
"type": "path",
|
||||||
"url": "../nulib",
|
"url": "../nulib",
|
||||||
"reference": "939f7726ab139071e8a3ef5c83fc147fba859d9d"
|
"reference": "2a50167241fe6b6d84d793aad748eb74c631eae9"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
@ -599,6 +599,8 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ext-pcntl": "*",
|
"ext-pcntl": "*",
|
||||||
|
"ext-pdo": "*",
|
||||||
|
"ext-pgsql": "*",
|
||||||
"ext-posix": "*",
|
"ext-posix": "*",
|
||||||
"ext-sqlite3": "*",
|
"ext-sqlite3": "*",
|
||||||
"nulib/tests": "^7.4"
|
"nulib/tests": "^7.4"
|
||||||
@ -637,7 +639,7 @@
|
|||||||
"dist": {
|
"dist": {
|
||||||
"type": "path",
|
"type": "path",
|
||||||
"url": "../nulib-phpss",
|
"url": "../nulib-phpss",
|
||||||
"reference": "a78623f5ae6891144b9581709847328b93342e1d"
|
"reference": "e902acb4461a9358de8cce0a534bfe0e63e8b100"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"nulib/php": "^7.4-dev",
|
"nulib/php": "^7.4-dev",
|
||||||
@ -681,7 +683,7 @@
|
|||||||
"dist": {
|
"dist": {
|
||||||
"type": "path",
|
"type": "path",
|
||||||
"url": "../nulib-spout",
|
"url": "../nulib-spout",
|
||||||
"reference": "f9c420058015d02e913f0d65c41242ec7b8a0cee"
|
"reference": "8d7512e2bff3dc333bb58130ac5500017db700ca"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php';
|
require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
use nur\sery\tools\SteamTrainApp;
|
use nulib\tools\SteamTrainApp;
|
||||||
|
|
||||||
SteamTrainApp::run();
|
SteamTrainApp::run();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
namespace nur\cli;
|
namespace nur\cli;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use nulib\app;
|
||||||
use nulib\app\RunFile;
|
use nulib\app\RunFile;
|
||||||
use nulib\ExitError;
|
use nulib\ExitError;
|
||||||
use nulib\ext\yaml;
|
use nulib\ext\yaml;
|
||||||
@ -16,7 +17,6 @@ use nur\config\ArrayConfig;
|
|||||||
use nur\msg;
|
use nur\msg;
|
||||||
use nur\os;
|
use nur\os;
|
||||||
use nur\path;
|
use nur\path;
|
||||||
use nur\sery\app;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Application: application de base
|
* Class Application: application de base
|
||||||
|
@ -15,10 +15,9 @@ use Throwable;
|
|||||||
* profil par défaut est dépendant de l'implémentation de IConfigManager
|
* profil par défaut est dépendant de l'implémentation de IConfigManager
|
||||||
* utilisée. Dans l'implémentation actuelle, 'ALL' est le profil par défaut.
|
* utilisée. Dans l'implémentation actuelle, 'ALL' est le profil par défaut.
|
||||||
* - PKEY est le chemin de clé dans lequel les caractères '.' sont remplacés
|
* - PKEY est le chemin de clé dans lequel les caractères '.' sont remplacés
|
||||||
* par '__' et '-' par '_' (celà signifie qu'il n'est pas possible de définir
|
* par '__'
|
||||||
* un chemin de clé qui contient le caractère '_')
|
|
||||||
*
|
*
|
||||||
* par exemple, la valeur dbs.my-auth.type du profil par défaut est pris dans
|
* par exemple, la valeur dbs.my_auth.type du profil par défaut est pris dans
|
||||||
* la variable 'CONFIG_ALL_dbs__my_auth__type'. pour le profil prod c'est la
|
* la variable 'CONFIG_ALL_dbs__my_auth__type'. pour le profil prod c'est la
|
||||||
* variable 'CONFIG_prod_dbs__my_auth__type'
|
* variable 'CONFIG_prod_dbs__my_auth__type'
|
||||||
*
|
*
|
||||||
@ -51,7 +50,9 @@ class EnvConfig extends DynConfig {
|
|||||||
$profile = substr($name, 0, $i);
|
$profile = substr($name, 0, $i);
|
||||||
$name = substr($name, $i + 1);
|
$name = substr($name, $i + 1);
|
||||||
$pkey = str_replace("__", ".", $name);
|
$pkey = str_replace("__", ".", $name);
|
||||||
$pkey = str_replace("_", "-", $pkey);
|
#XXX désactiver parce que les configurations sont plus généralement avec
|
||||||
|
# le caractères '_', par le caractères '-'
|
||||||
|
//$pkey = str_replace("_", "-", $pkey);
|
||||||
return [$pkey, $profile];
|
return [$pkey, $profile];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ class ref_type {
|
|||||||
/** comme {@link c} mais nullable */
|
/** comme {@link c} mais nullable */
|
||||||
const NCONTENT = "?".self::CONTENT;
|
const NCONTENT = "?".self::CONTENT;
|
||||||
|
|
||||||
/** comme {@link \nur\sery\FILE} mais nullable */
|
/** comme {@link FILE} mais nullable */
|
||||||
const NFILE = "?".self::FILE;
|
const NFILE = "?".self::FILE;
|
||||||
|
|
||||||
/** comme {@link DATETIME} mais nullable */
|
/** comme {@link DATETIME} mais nullable */
|
||||||
|
@ -32,6 +32,7 @@ class nb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final function menu($text, ?array $links=null, ?array $options=null): array {
|
static final function menu($text, ?array $links=null, ?array $options=null): array {
|
||||||
|
$links = array_filter($links, function($link) { return $link !== null; });
|
||||||
$item = ["item" => "menu", "links" => $links, "value" => $text];
|
$item = ["item" => "menu", "links" => $links, "value" => $text];
|
||||||
if ($options !== null) $item = array_merge($item, $options);
|
if ($options !== null) $item = array_merge($item, $options);
|
||||||
return $item;
|
return $item;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\v\vp;
|
namespace nur\v\vp;
|
||||||
|
|
||||||
|
use nulib\app;
|
||||||
use nur\authz;
|
use nur\authz;
|
||||||
use nur\b\authnz\IAuthzUser;
|
use nur\b\authnz\IAuthzUser;
|
||||||
use nur\config;
|
use nur\config;
|
||||||
@ -46,6 +47,21 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage {
|
|||||||
|
|
||||||
const MENU_SULOGIN = true;
|
const MENU_SULOGIN = true;
|
||||||
|
|
||||||
|
protected function getAppVersionNbtext(): ?array {
|
||||||
|
$app = app::get();
|
||||||
|
$projdir = $app->getProjdir();
|
||||||
|
$versionfile = "$projdir/VERSION.txt";
|
||||||
|
if (file_exists($versionfile)) {
|
||||||
|
$name = $app->getName();
|
||||||
|
$version = file_get_contents($versionfile);
|
||||||
|
return nb::text([
|
||||||
|
"style" => "margin: 0 15px",
|
||||||
|
"$name v$version"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getAuthzNbtext(IAuthzUser $user): array {
|
protected function getAuthzNbtext(IAuthzUser $user): array {
|
||||||
$username = $user->getUsername();
|
$username = $user->getUsername();
|
||||||
$role = $user->getRole();
|
$role = $user->getRole();
|
||||||
@ -95,6 +111,7 @@ class NavigablePage extends AInitAuthzPage implements INavigablePage {
|
|||||||
$user = authz::get();
|
$user = authz::get();
|
||||||
navbar::nav(["align" => "right"], [
|
navbar::nav(["align" => "right"], [
|
||||||
nb::menu(icon::user($user->getShortName()), [
|
nb::menu(icon::user($user->getShortName()), [
|
||||||
|
$this->getAppVersionNbtext(),
|
||||||
$this->getAuthzNbtext($user),
|
$this->getAuthzNbtext($user),
|
||||||
$this->getLogoutNblink(),
|
$this->getLogoutNblink(),
|
||||||
]),
|
]),
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
require(__DIR__.'/../../vendor/autoload.php');
|
require(__DIR__.'/../../vendor/autoload.php');
|
||||||
|
|
||||||
use nur\cli\Application;
|
use nur\cli\Application;
|
||||||
use nur\sery\output\msg;
|
use nulib\output\msg;
|
||||||
|
|
||||||
class TestArgs4 extends Application {
|
class TestArgs4 extends Application {
|
||||||
protected $query;
|
protected $query;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery;
|
namespace nulib;
|
||||||
|
|
||||||
use nulib\A;
|
use nulib\A;
|
||||||
use nulib\app\LockFile;
|
use nulib\app\LockFile;
|
||||||
@ -12,7 +12,7 @@ use nulib\php\func;
|
|||||||
use nulib\str;
|
use nulib\str;
|
||||||
use nulib\ValueException;
|
use nulib\ValueException;
|
||||||
use nur\cli\Application as nur_Application;
|
use nur\cli\Application as nur_Application;
|
||||||
use nur\sery\app\cli\Application;
|
use nulib\app\cli\Application;
|
||||||
|
|
||||||
class app {
|
class app {
|
||||||
private static function isa_Application($app): bool {
|
private static function isa_Application($app): bool {
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\app\cli;
|
namespace nulib\app\cli;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use nulib\app\RunFile;
|
use nulib\app\RunFile;
|
||||||
@ -13,7 +13,7 @@ use nulib\ValueException;
|
|||||||
use nur\cli\ArgsException;
|
use nur\cli\ArgsException;
|
||||||
use nur\cli\ArgsParser;
|
use nur\cli\ArgsParser;
|
||||||
use nur\config;
|
use nur\config;
|
||||||
use nur\sery\app;
|
use nulib\app;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Application: application de base
|
* Class Application: application de base
|
@ -1,13 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AbstractAccess: implémentation par défaut pour des instances standard
|
* Class AbstractAccess: implémentation par défaut pour des instances standard
|
||||||
* de {@link IAccess}
|
* de {@link IAccess}
|
||||||
*/
|
*/
|
||||||
abstract class AbstractAccess implements IAccess {
|
abstract class AbstractAccess implements IAccess {
|
||||||
|
const ALLOW_EMPTY = true;
|
||||||
|
|
||||||
|
function __construct(?array $params=null) {
|
||||||
|
$this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool $allowEmpty;
|
||||||
|
|
||||||
|
function isAllowEmpty(): bool {
|
||||||
|
return $this->allowEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
function inc(): int {
|
function inc(): int {
|
||||||
$value = (int)$this->get();
|
$value = (int)$this->get();
|
||||||
$this->set(++$value);
|
$this->set(++$value);
|
||||||
@ -33,4 +45,13 @@ abstract class AbstractAccess implements IAccess {
|
|||||||
cl::set($array, $key, $value);
|
cl::set($array, $key, $value);
|
||||||
$this->set($array);
|
$this->set($array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureAssoc(array $keys, ?array $params=null): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureOrder(array $keys, ?array $params=null): void {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
8
src/php/access/ArrayAccess.php
Normal file
8
src/php/access/ArrayAccess.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\php\access;
|
||||||
|
|
||||||
|
class ArrayAccess extends KeyAccess {
|
||||||
|
const ALLOW_NULL = true;
|
||||||
|
const ALLOW_FALSE = false;
|
||||||
|
const PROTECT_DEST = true;
|
||||||
|
}
|
182
src/php/access/ChainAccess.php
Normal file
182
src/php/access/ChainAccess.php
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\php\access;
|
||||||
|
|
||||||
|
use nulib\cl;
|
||||||
|
use nulib\StateException;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionException;
|
||||||
|
|
||||||
|
class ChainAccess extends AbstractAccess {
|
||||||
|
const ACCESS_AUTO = 0, ACCESS_KEY = 1, ACCESS_PROPERTY = 2;
|
||||||
|
|
||||||
|
private static function unexpected_access_type(): StateException {
|
||||||
|
return StateException::unexpected_state("access_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct(IAccess $access, $key, ?array $params=null) {
|
||||||
|
parent::__construct();
|
||||||
|
$this->access = $access;
|
||||||
|
$this->key = $key;
|
||||||
|
$this->accessType = $params["access_type"] ?? self::ACCESS_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IAccess $access;
|
||||||
|
|
||||||
|
/** @var null|int|string|array */
|
||||||
|
protected $key;
|
||||||
|
|
||||||
|
protected int $accessType;
|
||||||
|
|
||||||
|
protected function _accessType($access, $key): int {
|
||||||
|
$accessType = $this->accessType;
|
||||||
|
if ($accessType === self::ACCESS_AUTO) {
|
||||||
|
if (is_object($access) && is_string($key)) {
|
||||||
|
$accessType = self::ACCESS_PROPERTY;
|
||||||
|
} else {
|
||||||
|
$accessType = self::ACCESS_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $accessType;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _has(): bool {
|
||||||
|
$src = $this->access->get();
|
||||||
|
$key = $this->key;
|
||||||
|
$accessType = $this->_accessType($src, $key);
|
||||||
|
if ($accessType === self::ACCESS_KEY) {
|
||||||
|
return cl::phas($src, $key);
|
||||||
|
} elseif ($accessType === self::ACCESS_PROPERTY) {
|
||||||
|
$class = new ReflectionClass($src);
|
||||||
|
return $class->hasProperty($key) || property_exists($src, $key);
|
||||||
|
} else {
|
||||||
|
throw self::unexpected_access_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _get($default=null) {
|
||||||
|
$src = $this->access->get();
|
||||||
|
$key = $this->key;
|
||||||
|
$accessType = $this->_accessType($src, $key);
|
||||||
|
if ($accessType === self::ACCESS_KEY) {
|
||||||
|
return cl::pget($src, $key);
|
||||||
|
} elseif ($accessType === self::ACCESS_PROPERTY) {
|
||||||
|
$class = new ReflectionClass($src);
|
||||||
|
try {
|
||||||
|
$property = $class->getProperty($key);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
} catch (ReflectionException $e) {
|
||||||
|
$property = null;
|
||||||
|
}
|
||||||
|
if ($property !== null) {
|
||||||
|
return $property->getValue($src);
|
||||||
|
} elseif (property_exists($src, $key)) {
|
||||||
|
return $src->$key;
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw self::unexpected_access_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _pset(object $dest, $name, $value): void {
|
||||||
|
$class = new ReflectionClass($dest);
|
||||||
|
try {
|
||||||
|
$property = $class->getProperty($name);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
} catch (ReflectionException $e) {
|
||||||
|
$property = null;
|
||||||
|
}
|
||||||
|
if ($property !== null) {
|
||||||
|
$property->setValue($dest, $value);
|
||||||
|
} else {
|
||||||
|
$dest->$name = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAllowEmpty(): bool {
|
||||||
|
return $this->access->isAllowEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(): bool {
|
||||||
|
if (!$this->access->exists()) return false;
|
||||||
|
if ($this->key === null) return true;
|
||||||
|
return $this->_has();
|
||||||
|
}
|
||||||
|
|
||||||
|
function available(): bool {
|
||||||
|
if (!$this->access->available()) return false;
|
||||||
|
if ($this->key === null) return true;
|
||||||
|
if (!$this->_has()) return false;
|
||||||
|
return $this->isAllowEmpty() || $this->_get() !== "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($default=null) {
|
||||||
|
if ($this->key === null) {
|
||||||
|
return $this->access->get($default);
|
||||||
|
}
|
||||||
|
return $this->_get($default);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set($value): void {
|
||||||
|
if ($this->key === null) {
|
||||||
|
$this->access->set($value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dest = $this->access->get();
|
||||||
|
$key = $this->key;
|
||||||
|
$accessType = $this->_accessType($dest, $key);
|
||||||
|
if ($accessType === self::ACCESS_KEY) {
|
||||||
|
cl::pset($dest, $key, $value);
|
||||||
|
$this->access->set($dest);
|
||||||
|
} elseif ($accessType === self::ACCESS_PROPERTY) {
|
||||||
|
$this->_pset($dest, $key, $value);
|
||||||
|
} else {
|
||||||
|
throw self::unexpected_access_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function del(): void {
|
||||||
|
if ($this->key === null) {
|
||||||
|
$this->access->del();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dest = $this->access->get();
|
||||||
|
$key = $this->key;
|
||||||
|
$accessType = $this->_accessType($dest, $key);
|
||||||
|
if ($accessType === self::ACCESS_KEY) {
|
||||||
|
cl::pdel($dest, $key);
|
||||||
|
$this->access->set($dest);
|
||||||
|
} elseif ($accessType === self::ACCESS_PROPERTY) {
|
||||||
|
$this->_pset($dest, $key, null);
|
||||||
|
} else {
|
||||||
|
throw self::unexpected_access_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKey($key, ?array $params=null): IAccess {
|
||||||
|
if ($key === null) return $this;
|
||||||
|
$accessType = $params["access_type"] ?? $this->accessType;
|
||||||
|
if ($accessType === self::ACCESS_KEY && $accessType === $this->accessType) {
|
||||||
|
if ($this->key !== null) $key = cl::merge($this->key, $key);
|
||||||
|
return new ChainAccess($this->access, $key);
|
||||||
|
} else {
|
||||||
|
return new ChainAccess($this, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAssoc(array $keys, ?array $params=null): void {
|
||||||
|
#XXX fonction de $accessType?
|
||||||
|
#$this->access->ensureAssoc($keys, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void {
|
||||||
|
#XXX fonction de $accessType?
|
||||||
|
#$this->access->ensureKeys($defaults, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureOrder(array $keys, ?array $params=null): void {
|
||||||
|
#XXX fonction de $accessType?
|
||||||
|
#$this->access->ensureOrder($keys, $params);
|
||||||
|
}
|
||||||
|
}
|
@ -1,73 +1,105 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre
|
* Class FormAccess: accès à une valeur de $_POST puis $_GET, dans cet ordre
|
||||||
*/
|
*/
|
||||||
class FormAccess extends AbstractAccess {
|
class FormAccess extends AbstractAccess {
|
||||||
|
const ALLOW_EMPTY = false;
|
||||||
|
|
||||||
function __construct($key, ?array $params=null) {
|
function __construct($key, ?array $params=null) {
|
||||||
|
parent::__construct($params);
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
$this->allowEmpty = $params["allow_empty"] ?? false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var int|string */
|
/** @var null|int|string|array */
|
||||||
protected $key;
|
protected $key;
|
||||||
|
|
||||||
protected bool $allowEmpty;
|
protected function _exists(array $first, ?array $second=null): bool {
|
||||||
|
|
||||||
function exists(): bool {
|
|
||||||
$key = $this->key;
|
$key = $this->key;
|
||||||
if ($key === null) return false;
|
if ($key === null) return true;
|
||||||
return array_key_exists($key, $_POST) || array_key_exists($key, $_GET);
|
if (cl::phas($first, $key)) return true;
|
||||||
|
return $second !== null && cl::phas($second, $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function available(): bool {
|
function exists(): bool {
|
||||||
|
return $this->_exists($_POST, $_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _available(array $first, ?array $second=null): bool {
|
||||||
$key = $this->key;
|
$key = $this->key;
|
||||||
if ($key === null) return false;
|
if ($key === null) return true;
|
||||||
if (array_key_exists($key, $_POST)) {
|
if (cl::phas($first, $key)) {
|
||||||
return $this->allowEmpty || $_POST[$key] !== "";
|
return $this->allowEmpty || cl::pget($first, $key) !== "";
|
||||||
} elseif (array_key_exists($key, $_GET)) {
|
} elseif ($second !== null && cl::phas($second, $key)) {
|
||||||
return $this->allowEmpty || $_GET[$key] !== "";
|
return $this->allowEmpty || cl::pget($second, $key) !== "";
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function get($default=null) {
|
public function available(): bool {
|
||||||
|
return $this->_available($_POST, $_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _get($default, array $first, ?array $second=null) {
|
||||||
$key = $this->key;
|
$key = $this->key;
|
||||||
if ($key === null) return $default;
|
if ($key === null) return cl::merge($first, $second);
|
||||||
if (array_key_exists($key, $_POST)) {
|
if (cl::phas($first, $key)) {
|
||||||
$value = $_POST[$key];
|
$value = cl::pget($first, $key);
|
||||||
if ($value === "" && !$this->allowEmpty) return $default;
|
if ($value !== "" || $this->allowEmpty) return $value;
|
||||||
return $value;
|
} elseif ($second !== null && cl::phas($second, $key)) {
|
||||||
} elseif (array_key_exists($key, $_GET)) {
|
$value = cl::pget($second, $key);
|
||||||
$value = $_GET[$key];
|
if ($value !== "" || $this->allowEmpty) return $value;
|
||||||
if ($value === "" && !$this->allowEmpty) return $default;
|
}
|
||||||
return $value;
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($default=null) {
|
||||||
|
return $this->_get($default, $_POST, $_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _set($value, array &$first, ?array &$second=null): void {
|
||||||
|
$key = $this->key;
|
||||||
|
if ($key === null) {
|
||||||
|
# interdire la modification de la destination
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) {
|
||||||
|
cl::pset($second, $key, $value);
|
||||||
} else {
|
} else {
|
||||||
return $default;
|
cl::pset($first, $key, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function set($value): void {
|
function set($value): void {
|
||||||
|
$this->_set($value, $_POST, $_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _del(array &$first, ?array &$second=null): void {
|
||||||
$key = $this->key;
|
$key = $this->key;
|
||||||
if ($key === null) return;
|
if ($key === null) {
|
||||||
if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) {
|
# interdire la modification de la destination
|
||||||
cl::set($_GET, $key, $value);
|
return;
|
||||||
|
}
|
||||||
|
if ($second !== null && !cl::phas($first, $key) && cl::phas($second, $key)) {
|
||||||
|
cl::pdel($second, $key);
|
||||||
} else {
|
} else {
|
||||||
cl::set($_POST, $key, $value);
|
cl::pdel($first, $key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(): void {
|
function del(): void {
|
||||||
$key = $this->key;
|
$this->_del($_POST, $_GET);
|
||||||
if ($key === null) return;
|
}
|
||||||
if (!array_key_exists($key, $_POST) && array_key_exists($key, $_GET)) {
|
|
||||||
cl::del($_GET, $key);
|
function addKey($key): self {
|
||||||
} else {
|
if ($key === null) return $this;
|
||||||
cl::del($_POST, $key);
|
if ($this->key !== null) $key = cl::merge($this->key, $key);
|
||||||
}
|
return new static($key, [
|
||||||
|
"allow_empty" => $this->allowEmpty
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class GetAccess: accès à une valeur de $_GET
|
* Class GetAccess: accès à une valeur de $_GET
|
||||||
*/
|
*/
|
||||||
class GetAccess extends FormAccess {
|
class GetAccess extends FormAccess {
|
||||||
function exists(): bool {
|
function exists(): bool {
|
||||||
$key = $this->key;
|
return $this->_exists($_GET);
|
||||||
if ($key === null) return false;
|
|
||||||
return array_key_exists($key, $_GET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function available(): bool {
|
public function available(): bool {
|
||||||
$key = $this->key;
|
return $this->_available($_GET);
|
||||||
if ($key === null) return false;
|
|
||||||
if (array_key_exists($key, $_GET)) {
|
|
||||||
return $this->allowEmpty || $_GET[$key] !== "";
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function get($default=null) {
|
function get($default=null) {
|
||||||
$key = $this->key;
|
return $this->_get($default, $_GET);
|
||||||
if ($key === null) return $default;
|
|
||||||
if (array_key_exists($key, $_GET)) {
|
|
||||||
$value = $_GET[$key];
|
|
||||||
if ($value === "" && !$this->allowEmpty) return $default;
|
|
||||||
return $value;
|
|
||||||
} else {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function set($value): void {
|
function set($value): void {
|
||||||
$key = $this->key;
|
$this->_set($value, $_GET);
|
||||||
if ($key === null) return;
|
|
||||||
cl::set($_GET, $key, $value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(): void {
|
function del(): void {
|
||||||
$key = $this->key;
|
$this->_del($_GET);
|
||||||
if ($key === null) return;
|
|
||||||
cl::del($_GET, $key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
|
use ReflectionClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface IAccess: abstraction d'un accès complet à une valeur
|
* Interface IAccess: abstraction d'un accès complet à une valeur
|
||||||
*/
|
*/
|
||||||
interface IAccess extends IGetter, ISetter, IDeleter {
|
interface IAccess extends IGetter, ISetter, IDeleter {
|
||||||
/** incrémenter la valeur */
|
/** incrémenter la valeur et la retourner */
|
||||||
function inc(): int;
|
function inc(): int;
|
||||||
|
|
||||||
/** décrémenter la valeur */
|
/** décrémenter la valeur et la retourner */
|
||||||
function dec(bool $allowNegative=false): int;
|
function dec(bool $allowNegative=false): int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,4 +27,26 @@ interface IAccess extends IGetter, ISetter, IDeleter {
|
|||||||
* tableau si $key===null
|
* tableau si $key===null
|
||||||
*/
|
*/
|
||||||
function append($value, $key=null): void;
|
function append($value, $key=null): void;
|
||||||
|
|
||||||
|
/** retourner une instance permettant d'accéder à $value[$key] */
|
||||||
|
function addKey($key): IAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* s'assurer que la destination est un tableau associatif en remplaçant les
|
||||||
|
* clés numériques par les clés correspondantes du tableau $keys
|
||||||
|
*/
|
||||||
|
function ensureAssoc(array $keys, ?array $params=null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* s'assurer que toutes les clés mentionnées dans le tableau $defaults
|
||||||
|
* existent. si elles n'existent pas, leur donner la valeur du tableau
|
||||||
|
* $defaults
|
||||||
|
*/
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* s'assure que les clés de la destination sont dans l'ordre mentionné dans le
|
||||||
|
* tableau $keys. toutes les clés supplémentaires sont placées à la fin
|
||||||
|
*/
|
||||||
|
function ensureOrder(array $keys, ?array $params=null): void;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class IDeleter: une abstraction d'un objet qui permet de supprimer une valeur
|
* Class IDeleter: une abstraction d'un objet qui permet de supprimer une valeur
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class IGetter: une abstraction d'un objet qui permet d'obtenir une valeur
|
* Class IGetter: une abstraction d'un objet qui permet d'obtenir une valeur
|
||||||
@ -11,6 +11,12 @@ interface IGetter {
|
|||||||
*/
|
*/
|
||||||
function exists(): bool;
|
function exists(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool true si cet objet autorise les chaines vides. si c'est le cas,
|
||||||
|
* {@link exists()} et {@link available()} sont fonctionnellement identiques
|
||||||
|
*/
|
||||||
|
function isAllowEmpty(): bool;
|
||||||
|
|
||||||
/** @return bool true si la valeur existe et est utilisable, false sinon */
|
/** @return bool true si la valeur existe et est utilisable, false sinon */
|
||||||
function available(): bool;
|
function available(): bool;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ISetter: une abstraction d'un objet qui permet de modifier une valeur
|
* Class ISetter: une abstraction d'un objet qui permet de modifier une valeur
|
||||||
|
@ -1,69 +1,188 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
use ArrayAccess;
|
use ArrayAccess;
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
|
use nulib\ref\schema\ref_schema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class KeyAccess: accès à une valeur d'une clé dans un tableau
|
* Class KeyAccess: accès
|
||||||
|
* - soit à une valeur d'un chemin de clé dans un tableau (si $key !== null)
|
||||||
|
* - soit à une valeur scalaire (si $key === null)
|
||||||
*/
|
*/
|
||||||
class KeyAccess extends AbstractAccess {
|
class KeyAccess extends AbstractAccess {
|
||||||
function __construct(&$dest, $key, ?array $params=null) {
|
const ALLOW_NULL = null;
|
||||||
|
const ALLOW_FALSE = null;
|
||||||
|
const PROTECT_DEST = false;
|
||||||
|
|
||||||
|
function __construct(&$dest, $key=null, ?array $params=null) {
|
||||||
|
parent::__construct($params);
|
||||||
|
$this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST;
|
||||||
$this->dest =& $dest;
|
$this->dest =& $dest;
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
$this->allowNull = $params["allow_null"] ?? true;
|
$this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL;
|
||||||
$this->allowFalse = $params["allow_false"] ?? false;
|
$this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE;
|
||||||
$this->allowEmpty = $params["allow_empty"] ?? true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var array|ArrayAccess */
|
protected bool $protectDest;
|
||||||
|
|
||||||
|
/** @var mixed|array|ArrayAccess */
|
||||||
protected $dest;
|
protected $dest;
|
||||||
|
|
||||||
function reset(&$dest): self {
|
/** @var null|int|string|array */
|
||||||
|
protected $key;
|
||||||
|
|
||||||
|
function reset(&$dest, $key=null): self {
|
||||||
$this->dest =& $dest;
|
$this->dest =& $dest;
|
||||||
|
$this->key = $key;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var int|string */
|
function resetKey($key=null): self {
|
||||||
protected $key;
|
$this->key = $key;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected bool $allowNull;
|
protected ?bool $allowNull;
|
||||||
|
|
||||||
protected bool $allowFalse;
|
protected function isAllowNull(): bool {
|
||||||
|
$allowNull = $this->allowNull;
|
||||||
|
if ($allowNull !== null) return $allowNull;
|
||||||
|
return $this->key !== null;
|
||||||
|
}
|
||||||
|
|
||||||
protected bool $allowEmpty;
|
protected ?bool $allowFalse;
|
||||||
|
|
||||||
|
protected function isAllowFalse(): bool {
|
||||||
|
$allowFalse = $this->allowFalse;
|
||||||
|
if ($allowFalse !== null) return $allowFalse;
|
||||||
|
return $this->key === null;
|
||||||
|
}
|
||||||
|
|
||||||
function exists(): bool {
|
function exists(): bool {
|
||||||
$key = $this->key;
|
$key = $this->key;
|
||||||
if ($key === null) return false;
|
if ($key === null) {
|
||||||
return cl::has($this->dest, $key);
|
return $this->isAllowNull() || $this->dest !== null;
|
||||||
|
} else {
|
||||||
|
return cl::phas($this->dest, $key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function available(): bool {
|
function available(): bool {
|
||||||
if (!$this->exists()) return false;
|
if (!$this->exists()) return false;
|
||||||
$value = cl::get($this->dest, $this->key);
|
$key = $this->key;
|
||||||
if ($value === null) return $this->allowNull;
|
if ($key === null) $value = $this->dest;
|
||||||
if ($value === false) return $this->allowFalse;
|
else $value = cl::pget($this->dest, $key);
|
||||||
if ($value === "") return $this->allowEmpty;
|
if ($value === "") return $this->allowEmpty;
|
||||||
|
if ($value === null) return $this->isAllowNull();
|
||||||
|
if ($value === false) return $this->isAllowFalse();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get($default=null) {
|
function get($default=null) {
|
||||||
if ($this->key === null) return $default;
|
$key = $this->key;
|
||||||
$value = cl::get($this->dest, $this->key, $default);
|
if ($key === null) $value = $this->dest;
|
||||||
if ($value === null && !$this->allowNull) return $default;
|
else $value = cl::pget($this->dest, $key, $default);
|
||||||
if ($value === false && !$this->allowFalse) return $default;
|
|
||||||
if ($value === "" && !$this->allowEmpty) return $default;
|
if ($value === "" && !$this->allowEmpty) return $default;
|
||||||
|
if ($value === null && !$this->isAllowNull()) return $default;
|
||||||
|
if ($value === false && !$this->isAllowFalse()) return $default;
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set($value): void {
|
function set($value): void {
|
||||||
if ($this->key === null) return;
|
$key = $this->key;
|
||||||
cl::set($this->dest, $this->key, $value);
|
if ($key === null) {
|
||||||
|
if (!$this->protectDest) $this->dest = $value;
|
||||||
|
} else {
|
||||||
|
cl::pset($this->dest, $key, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(): void {
|
function del(): void {
|
||||||
if ($this->key === null) return;
|
$key = $this->key;
|
||||||
cl::del($this->dest, $this->key);
|
if ($key === null) {
|
||||||
|
if (!$this->protectDest) $this->dest = null;
|
||||||
|
} else {
|
||||||
|
cl::pdel($this->dest, $key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKey($key): self {
|
||||||
|
if ($key === null) return $this;
|
||||||
|
if ($this->key !== null) $key = cl::merge($this->key, $key);
|
||||||
|
return new KeyAccess($this->dest, $key, [
|
||||||
|
"allow_empty" => $this->allowEmpty,
|
||||||
|
"allow_null" => $this->allowNull,
|
||||||
|
"allow_false" => $this->allowFalse,
|
||||||
|
"protect_dest" => $this->protectDest,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAssoc(array $keys, ?array $params=null): void {
|
||||||
|
$dest =& $this->dest;
|
||||||
|
$prefix = $params["key_prefix"] ?? null;
|
||||||
|
$suffix = $params["key_suffix"] ?? null;
|
||||||
|
$index = 0;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if ($prefix !== null || $suffix !== null) {
|
||||||
|
$destKey = "$prefix$key$suffix";
|
||||||
|
} else {
|
||||||
|
# préserver les clés numériques
|
||||||
|
$destKey = $key;
|
||||||
|
}
|
||||||
|
if ($dest !== null && array_key_exists($destKey, $dest)) continue;
|
||||||
|
while (in_array($index, $keys, true)) {
|
||||||
|
$index++;
|
||||||
|
}
|
||||||
|
if ($dest !== null && array_key_exists($index, $dest)) {
|
||||||
|
$dest[$destKey] = $dest[$index];
|
||||||
|
unset($dest[$index]);
|
||||||
|
$index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void {
|
||||||
|
$dest =& $this->dest;
|
||||||
|
$keys = array_keys($defaults);
|
||||||
|
$prefix = $params["key_prefix"] ?? null;
|
||||||
|
$suffix = $params["key_suffix"] ?? null;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$destKey = "$prefix$key$suffix";
|
||||||
|
if ($dest === null || !array_key_exists($destKey, $dest)) {
|
||||||
|
$dest[$destKey] = $defaults[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureOrder(array $keys, ?array $params=null): void {
|
||||||
|
$dest =& $this->dest;
|
||||||
|
if ($dest === null) return;
|
||||||
|
|
||||||
|
$prefix = $params["key_prefix"] ?? null;
|
||||||
|
$suffix = $params["key_suffix"] ?? null;
|
||||||
|
if ($prefix !== null || $suffix !== null) {
|
||||||
|
foreach ($keys as &$key) {
|
||||||
|
$key = "$prefix$key$suffix";
|
||||||
|
}; unset($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$destKeys = array_keys($dest);
|
||||||
|
$keyCount = count($keys);
|
||||||
|
if (array_slice($destKeys, 0, $keyCount) === $keys) {
|
||||||
|
# si le tableau a déjà les bonnes clés dans le bon ordre, rien à faire
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ordered = [];
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (array_key_exists($key, $dest)) {
|
||||||
|
$ordered[$key] = $dest[$key];
|
||||||
|
unset($dest[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$preserveKeys = $params["preserve_keys"] ?? false;
|
||||||
|
if ($preserveKeys) $dest = cl::merge2($ordered, $dest);
|
||||||
|
else $dest = array_merge($ordered, $dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class PostAccess: accès à une valeur de $_POST
|
* Class PostAccess: accès à une valeur de $_POST
|
||||||
*/
|
*/
|
||||||
class PostAccess extends FormAccess {
|
class PostAccess extends FormAccess {
|
||||||
function exists(): bool {
|
function exists(): bool {
|
||||||
$key = $this->key;
|
return $this->_exists($_POST);
|
||||||
if ($key === null) return false;
|
|
||||||
return array_key_exists($key, $_POST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function available(): bool {
|
public function available(): bool {
|
||||||
$key = $this->key;
|
return $this->_available($_POST);
|
||||||
if ($key === null) return false;
|
|
||||||
if (array_key_exists($key, $_POST)) {
|
|
||||||
return $this->allowEmpty || $_POST[$key] !== "";
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function get($default=null) {
|
function get($default=null) {
|
||||||
$key = $this->key;
|
return $this->_get($default, $_POST);
|
||||||
if ($key === null) return $default;
|
|
||||||
if (array_key_exists($key, $_POST)) {
|
|
||||||
$value = $_POST[$key];
|
|
||||||
if ($value === "" && !$this->allowEmpty) return $default;
|
|
||||||
return $value;
|
|
||||||
} else {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function set($value): void {
|
function set($value): void {
|
||||||
$key = $this->key;
|
$this->_set($value, $_POST);
|
||||||
if ($key === null) return;
|
|
||||||
cl::set($_POST, $key, $value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function del(): void {
|
function del(): void {
|
||||||
$key = $this->key;
|
$this->_del($_POST);
|
||||||
if ($key === null) return;
|
|
||||||
cl::del($_POST, $key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
173
src/php/access/PropertyAccess.php
Normal file
173
src/php/access/PropertyAccess.php
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\php\access;
|
||||||
|
|
||||||
|
use nulib\StateException;
|
||||||
|
use nulib\str;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionException;
|
||||||
|
use ReflectionProperty;
|
||||||
|
|
||||||
|
class PropertyAccess extends AbstractAccess {
|
||||||
|
const PROTECT_DEST = true;
|
||||||
|
const MAP_NAMES = true;
|
||||||
|
const ALLOW_NULL = true;
|
||||||
|
const ALLOW_FALSE = false;
|
||||||
|
|
||||||
|
function __construct(?object $dest, ?string $name=null, ?array $params=null) {
|
||||||
|
parent::__construct($params);
|
||||||
|
$this->protectDest = $params["protect_dest"] ?? static::PROTECT_DEST;
|
||||||
|
$this->mapNames = $params["map_names"] ?? static::MAP_NAMES;
|
||||||
|
$this->_setName($name);
|
||||||
|
$this->_setDest($dest);
|
||||||
|
$this->allowNull = $params["allow_null"] ?? static::ALLOW_NULL;
|
||||||
|
$this->allowFalse = $params["allow_false"] ?? static::ALLOW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool $protectDest;
|
||||||
|
|
||||||
|
protected ?object $dest;
|
||||||
|
protected bool $mapNames;
|
||||||
|
|
||||||
|
protected ?string $name;
|
||||||
|
|
||||||
|
protected ?ReflectionProperty $property;
|
||||||
|
|
||||||
|
private function _getName(string $key): string {
|
||||||
|
return $this->mapNames? str::us2camel($key): $key;
|
||||||
|
}
|
||||||
|
private function _setName(?string $name): void {
|
||||||
|
if ($name !== null) $name = $this->_getName($name);
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _getProperty(?string $name, ?ReflectionClass $class, ?object $object=null): ?ReflectionProperty {
|
||||||
|
$property = null;
|
||||||
|
if ($class === null && $object !== null) {
|
||||||
|
$class = new ReflectionClass($object);
|
||||||
|
}
|
||||||
|
if ($class !== null && $name !== null) {
|
||||||
|
try {
|
||||||
|
$property = $class->getProperty($name);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
} catch (ReflectionException $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $property;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _setDest(?object $dest): void {
|
||||||
|
$this->dest = $dest;
|
||||||
|
$this->property = $this->_getProperty($this->name, null, $dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(?object $dest, ?string $name=null): self {
|
||||||
|
$this->_setName($name);
|
||||||
|
$this->_setDest($dest);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetKey($name=null): self {
|
||||||
|
$this->_setName($name);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool $allowNull;
|
||||||
|
|
||||||
|
protected bool $allowFalse;
|
||||||
|
|
||||||
|
function exists(): bool {
|
||||||
|
$name = $this->name;
|
||||||
|
if ($this->dest === null) return false;
|
||||||
|
return $name === null
|
||||||
|
|| $this->property !== null
|
||||||
|
|| property_exists($this->dest, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _get($default=null) {
|
||||||
|
$name = $this->name;
|
||||||
|
$property = $this->property;
|
||||||
|
if ($this->dest === null) {
|
||||||
|
return $default;
|
||||||
|
} elseif ($name === null) {
|
||||||
|
return $this->dest;
|
||||||
|
} elseif ($property !== null) {
|
||||||
|
return $property->getValue($this->dest);
|
||||||
|
} elseif (property_exists($this->dest, $name)) {
|
||||||
|
return $this->dest->$name;
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function available(): bool {
|
||||||
|
if (!$this->exists()) return false;
|
||||||
|
$value = $this->_get();
|
||||||
|
if ($value === "") return $this->allowEmpty;
|
||||||
|
if ($value === null) return $this->allowNull;
|
||||||
|
if ($value === false) return $this->allowFalse;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($default=null) {
|
||||||
|
if (!$this->exists()) return $default;
|
||||||
|
$value = $this->_get();
|
||||||
|
if ($value === "" && !$this->allowEmpty) return $default;
|
||||||
|
if ($value === null && !$this->allowNull) return $default;
|
||||||
|
if ($value === false && !$this->allowFalse) return $default;
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _set($value): void {
|
||||||
|
$name = $this->name;
|
||||||
|
$property = $this->property;
|
||||||
|
if ($this->dest === null) {
|
||||||
|
throw StateException::unexpected_state("dest is null");
|
||||||
|
} elseif ($name === null) {
|
||||||
|
if (!$this->protectDest) $this->_setDest($value);
|
||||||
|
} elseif ($property !== null) {
|
||||||
|
$property->setValue($this->dest, $value);
|
||||||
|
} else {
|
||||||
|
$this->dest->$name = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set($value): void {
|
||||||
|
$this->_set($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function del(): void {
|
||||||
|
$this->_set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKey($key): IAccess {
|
||||||
|
if ($key === null) return $this;
|
||||||
|
return new ChainAccess($this, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void {
|
||||||
|
$dest = $this->dest;
|
||||||
|
if ($dest === null) {
|
||||||
|
# comme ne connait pas la classe de l'objet destination, on n'essaie pas
|
||||||
|
# de le créer
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$class = new ReflectionClass($dest);
|
||||||
|
$keys = array_keys($defaults);
|
||||||
|
$prefix = $params["key_prefix"] ?? null;
|
||||||
|
$suffix = $params["key_suffix"] ?? null;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$name = $this->_getName("$prefix$key$suffix");
|
||||||
|
$property = $this->_getProperty($name, $class);
|
||||||
|
if ($property !== null) {
|
||||||
|
$type = $property->getType();
|
||||||
|
if ($type !== null && !$property->isInitialized($dest) && $type->allowsNull()) {
|
||||||
|
# initialiser avec null au lieu de $defaults[$key] pour respecter le
|
||||||
|
# type de la propriété
|
||||||
|
$property->setValue($dest, null);
|
||||||
|
}
|
||||||
|
} elseif (!property_exists($dest, $name)) {
|
||||||
|
$dest->$name = $defaults[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ShadowAccess: accès en lecture depuis une instance de {@link IAccess}
|
* Class ShadowAccess: accès en lecture depuis une instance de {@link IAccess}
|
||||||
@ -16,6 +16,7 @@ namespace nur\sery\wip\php\access;
|
|||||||
*/
|
*/
|
||||||
class ShadowAccess extends AbstractAccess {
|
class ShadowAccess extends AbstractAccess {
|
||||||
function __construct(IAccess $reader, IAccess $writer) {
|
function __construct(IAccess $reader, IAccess $writer) {
|
||||||
|
parent::__construct();
|
||||||
$this->reader = $reader;
|
$this->reader = $reader;
|
||||||
$this->writer = $writer;
|
$this->writer = $writer;
|
||||||
$this->getter = $reader;
|
$this->getter = $reader;
|
||||||
@ -27,6 +28,10 @@ class ShadowAccess extends AbstractAccess {
|
|||||||
|
|
||||||
protected IGetter $getter;
|
protected IGetter $getter;
|
||||||
|
|
||||||
|
public function isAllowEmpty(): bool {
|
||||||
|
return $this->getter->isAllowEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
function exists(): bool {
|
function exists(): bool {
|
||||||
return $this->getter->exists();
|
return $this->getter->exists();
|
||||||
}
|
}
|
||||||
@ -48,4 +53,20 @@ class ShadowAccess extends AbstractAccess {
|
|||||||
$this->writer->del();
|
$this->writer->del();
|
||||||
$this->getter = $this->reader;
|
$this->getter = $this->reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addKey($key): IAccess {
|
||||||
|
return new ChainAccess($this, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAssoc(array $keys, ?array $params=null): void {
|
||||||
|
$this->writer->ensureAssoc($keys, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void {
|
||||||
|
$this->writer->ensureKeys($defaults, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureOrder(array $keys, ?array $params=null): void {
|
||||||
|
$this->writer->ensureOrder($keys, $params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,56 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\access;
|
namespace nulib\php\access;
|
||||||
|
|
||||||
/**
|
class ValueAccess extends KeyAccess {
|
||||||
* Class ValueAccess: accès à une valeur unitaire
|
const ALLOW_NULL = false;
|
||||||
*/
|
const ALLOW_FALSE = true;
|
||||||
class ValueAccess extends AbstractAccess {
|
const PROTECT_DEST = false;
|
||||||
function __construct(&$dest, ?array $params=null) {
|
|
||||||
$this->dest =& $dest;
|
|
||||||
$this->allowNull = $params["allow_null"] ?? false;
|
|
||||||
$this->allowFalse = $params["allow_false"] ?? true;
|
|
||||||
$this->allowEmpty = $params["allow_empty"] ?? true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var mixed */
|
|
||||||
protected $dest;
|
|
||||||
|
|
||||||
function reset(&$dest): self {
|
|
||||||
$this->dest =& $dest;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool $allowNull;
|
|
||||||
|
|
||||||
protected bool $allowFalse;
|
|
||||||
|
|
||||||
protected bool $allowEmpty;
|
|
||||||
|
|
||||||
function exists(): bool {
|
|
||||||
return $this->allowNull || $this->dest !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function available(): bool {
|
|
||||||
if (!$this->exists()) return false;
|
|
||||||
$value = $this->dest;
|
|
||||||
if ($value === false) return $this->allowFalse;
|
|
||||||
if ($value === "") return $this->allowEmpty;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get($default=null) {
|
|
||||||
$value = $this->dest;
|
|
||||||
if ($value === null && !$this->allowNull) return $default;
|
|
||||||
if ($value === false && !$this->allowFalse) return $default;
|
|
||||||
if ($value === "" && !$this->allowEmpty) return $default;
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function set($value): void {
|
|
||||||
$this->dest = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function del(): void {
|
|
||||||
$this->dest = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\php\coll;
|
namespace nulib\php\coll;
|
||||||
|
|
||||||
use Iterator;
|
use Iterator;
|
||||||
|
use IteratorAggregate;
|
||||||
use nulib\cl;
|
use nulib\cl;
|
||||||
use nulib\php\func;
|
use nulib\php\func;
|
||||||
use nur\sery\wip\php\iter;
|
use nulib\php\iter;
|
||||||
use Traversable;
|
use Traversable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,7 +34,7 @@ class Cursor implements Iterator {
|
|||||||
* alors retourner le tableau
|
* alors retourner le tableau
|
||||||
* ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null]
|
* ["a" => $row["a"], "b" => $row["x"], "c" => "y", "d" => null]
|
||||||
*/
|
*/
|
||||||
private static function map_row(array $row, ?array $map): array {
|
protected static function map_row(array $row, ?array $map): array {
|
||||||
if ($map === null) return $row;
|
if ($map === null) return $row;
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$mapped = [];
|
$mapped = [];
|
||||||
@ -48,7 +49,7 @@ class Cursor implements Iterator {
|
|||||||
$mapped[$key] = $func->invoke([$value, $key, $row]);
|
$mapped[$key] = $func->invoke([$value, $key, $row]);
|
||||||
} else {
|
} else {
|
||||||
if ($value === null) $mapped[$key] = null;
|
if ($value === null) $mapped[$key] = null;
|
||||||
else $mapped[$key] = cl::get($row, $key);
|
else $mapped[$key] = cl::get($row, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $mapped;
|
return $mapped;
|
||||||
@ -67,7 +68,7 @@ class Cursor implements Iterator {
|
|||||||
* - une valeur associative $key => $value indique que la clé correspondante
|
* - une valeur associative $key => $value indique que la clé correspondante
|
||||||
* doit exiter avec la valeur spécifiée
|
* doit exiter avec la valeur spécifiée
|
||||||
*/
|
*/
|
||||||
private static function filter_row(array $row, $filter): bool {
|
protected static function filter_row(array $row, $filter): bool {
|
||||||
if ($filter === null) return false;
|
if ($filter === null) return false;
|
||||||
if (!is_array($filter)) $filter = [$filter];
|
if (!is_array($filter)) $filter = [$filter];
|
||||||
if (!$filter) return false;
|
if (!$filter) return false;
|
||||||
@ -79,12 +80,12 @@ class Cursor implements Iterator {
|
|||||||
if (!array_key_exists($value, $row)) return false;
|
if (!array_key_exists($value, $row)) return false;
|
||||||
} elseif (is_bool($value)) {
|
} elseif (is_bool($value)) {
|
||||||
if ($value) {
|
if ($value) {
|
||||||
if (!array_key_exists($value, $row)) return false;
|
if (!array_key_exists($key, $row)) return false;
|
||||||
} else {
|
} else {
|
||||||
if (array_key_exists($value, $row)) return false;
|
if (array_key_exists($key, $row)) return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!array_key_exists($value, $row)) return false;
|
if (!array_key_exists($key, $row)) return false;
|
||||||
if ($row[$key] !== $value) return false;
|
if ($row[$key] !== $value) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,6 +115,11 @@ class Cursor implements Iterator {
|
|||||||
$this->rowsGenerator = $rowsGenerator;
|
$this->rowsGenerator = $rowsGenerator;
|
||||||
$this->rowsFunc = $rowsFunc;
|
$this->rowsFunc = $rowsFunc;
|
||||||
|
|
||||||
|
$this->cols = $params["cols"] ?? null;
|
||||||
|
$colsFunc = $params["cols_func"] ?? null;
|
||||||
|
if ($colsFunc !== null) $colsFunc = func::with($colsFunc);
|
||||||
|
$this->colsFunc = $colsFunc;
|
||||||
|
|
||||||
$map = $params["map"] ?? null;
|
$map = $params["map"] ?? null;
|
||||||
$mapFunc = $params["map_func"] ?? null;
|
$mapFunc = $params["map_func"] ?? null;
|
||||||
if ($mapFunc !== null) {
|
if ($mapFunc !== null) {
|
||||||
@ -140,16 +146,16 @@ class Cursor implements Iterator {
|
|||||||
/** un générateur de lignes */
|
/** un générateur de lignes */
|
||||||
private ?Traversable $rowsGenerator;
|
private ?Traversable $rowsGenerator;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor):?iterable</code> */
|
/** une fonction de signature <code>function(Cursor): ?iterable</code> */
|
||||||
private ?func $rowsFunc;
|
private ?func $rowsFunc;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor):?array</code> */
|
/** une fonction de signature <code>function(Cursor): ?array</code> */
|
||||||
private ?func $colsFunc;
|
private ?func $colsFunc;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor):?array</code> */
|
/** une fonction de signature <code>function(Cursor): ?array</code> */
|
||||||
private ?func $mapFunc;
|
private ?func $mapFunc;
|
||||||
|
|
||||||
/** une fonction de signature <code>function(Cursor):bool</code> */
|
/** une fonction de signature <code>function(Cursor): bool</code> */
|
||||||
private ?func $filterFunc;
|
private ?func $filterFunc;
|
||||||
|
|
||||||
protected ?iterable $rows;
|
protected ?iterable $rows;
|
||||||
@ -173,6 +179,10 @@ class Cursor implements Iterator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function cols(): ?array {
|
||||||
|
return $this->row !== null? array_keys($this->row): null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function filter(): bool {
|
protected function filter(): bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -185,20 +195,24 @@ class Cursor implements Iterator {
|
|||||||
# Iterator
|
# Iterator
|
||||||
|
|
||||||
function rewind() {
|
function rewind() {
|
||||||
|
$this->cols = null;
|
||||||
$this->index = 0;
|
$this->index = 0;
|
||||||
$this->origIndex = 0;
|
$this->origIndex = 0;
|
||||||
$this->key = null;
|
$this->key = null;
|
||||||
$this->raw = null;
|
$this->raw = null;
|
||||||
$this->row = null;
|
$this->row = null;
|
||||||
if ($this->rowsGenerator !== null) {
|
if ($this->rowsGenerator !== null) {
|
||||||
$this->rows = $this->rowsGenerator;
|
$rows = $this->rowsGenerator;
|
||||||
$this->rows->rewind();
|
if ($rows instanceof IteratorAggregate) $rows = $rows->getIterator();
|
||||||
|
$rows->rewind();
|
||||||
|
$this->rows = $rows;
|
||||||
} else {
|
} else {
|
||||||
$this->rows = $this->rowsFunc->invoke();
|
$this->rows = $this->rowsFunc->invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function valid() {
|
function valid(): bool {
|
||||||
|
$cols = $this->colsFunc;
|
||||||
$filter = $this->filterFunc;
|
$filter = $this->filterFunc;
|
||||||
$map = $this->mapFunc;
|
$map = $this->mapFunc;
|
||||||
while ($valid = iter::valid($this->rows)) {
|
while ($valid = iter::valid($this->rows)) {
|
||||||
@ -210,6 +224,10 @@ class Cursor implements Iterator {
|
|||||||
if (!$filtered) {
|
if (!$filtered) {
|
||||||
if ($map === null) $this->row = $this->map();
|
if ($map === null) $this->row = $this->map();
|
||||||
else $this->row = $map->invoke([$this]);
|
else $this->row = $map->invoke([$this]);
|
||||||
|
if ($this->cols === null) {
|
||||||
|
if ($cols === null) $this->cols = $this->cols();
|
||||||
|
else $this->cols = $cols->invoke([$this]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
iter::next($this->rows);
|
iter::next($this->rows);
|
||||||
@ -219,6 +237,7 @@ class Cursor implements Iterator {
|
|||||||
if (!$valid) {
|
if (!$valid) {
|
||||||
iter::close($this->rows);
|
iter::close($this->rows);
|
||||||
$this->rows = null;
|
$this->rows = null;
|
||||||
|
$this->cols = null;
|
||||||
$this->index = -1;
|
$this->index = -1;
|
||||||
$this->origIndex = -1;
|
$this->origIndex = -1;
|
||||||
$this->key = null;
|
$this->key = null;
|
||||||
@ -228,7 +247,7 @@ class Cursor implements Iterator {
|
|||||||
return $valid;
|
return $valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function current() {
|
function current(): ?array {
|
||||||
return $this->row;
|
return $this->row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php # -*- coding: utf-8 mode: php -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
|
<?php # -*- coding: utf-8 mode: php -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8
|
||||||
namespace nur\sery\wip\php;
|
namespace nulib\php;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Generator;
|
use Generator;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema;
|
namespace nulib\schema;
|
||||||
|
|
||||||
class OldSchema {
|
class OldSchema {
|
||||||
/**
|
/**
|
||||||
|
@ -1,70 +1,60 @@
|
|||||||
# nur\sery\schema
|
# nulib\schema
|
||||||
|
|
||||||
objet: s'assurer que des données soit dans un type particulier, en les
|
les classes de ce package permettent de s'assurer que des données soit dans un
|
||||||
convertissant si nécessaire. la source de ces données peut-être diverse:
|
type particulier, en les convertissant si nécessaire. la source de ces données
|
||||||
formulaire web, résultat d'une requête SQL, flux CSV, etc.
|
peut-être diverse: formulaire web, résultat d'une requête SQL, flux CSV, etc.
|
||||||
|
|
||||||
les données dont on peut modéliser le schéma sont de 3 types:
|
les données dont on peut modéliser le schéma sont de 3 types: scalaire, tableau
|
||||||
* scalaire
|
associatif ou liste. la nature du schéma (la valeur de la clé `"""`) indique le
|
||||||
* tableau associatif
|
type de donnée modélisée
|
||||||
* liste (tableau séquentiel ou associatif d'éléments du même type)
|
* donnée scalaire
|
||||||
|
|
||||||
chaque type de données a une syntaxe spécifique pour la définition du schéma.
|
forme courante:
|
||||||
|
~~~php
|
||||||
## Nature de schéma
|
const SCALAR_SCHEMA = [
|
||||||
|
$type, [$default, $title, ...]
|
||||||
Un schéma se présente sous la forme d'un tableau associatif avec des clés qui
|
];
|
||||||
dépendent de la nature du schéma. La nature du schéma est indiquée avec la clé
|
~~~
|
||||||
`""` (chaine vide), e.g
|
forme normalisée:
|
||||||
~~~php
|
|
||||||
const SCHEMA = [
|
|
||||||
"" => NATURE,
|
|
||||||
];
|
|
||||||
~~~
|
|
||||||
|
|
||||||
La nature indique le type de données représenté par le schéma.
|
|
||||||
* nature scalaire: modélise une donnée scalaire
|
|
||||||
~~~php
|
~~~php
|
||||||
const SCALAR_SCHEMA = [
|
const SCALAR_SCHEMA = [
|
||||||
$type, [$default, $title, ...]
|
$type, [$default, $title, ...]
|
||||||
"" => "scalar",
|
"" => "scalar",
|
||||||
];
|
];
|
||||||
~~~
|
~~~
|
||||||
Si le type est "array" ou "?array", on peut préciser le schéma de la donnée
|
* tableau associatif
|
||||||
~~~php
|
le tableau modélisé peut avoir des clés numériques ou chaines --> seules les
|
||||||
const SCALAR_SCHEMA = [
|
clés décrites par le schéma sont validées
|
||||||
"?array", [$default, $title, ...]
|
|
||||||
"" => "scalar",
|
forme courante:
|
||||||
"schema" => NAKED_SCHEMA,
|
|
||||||
];
|
|
||||||
~~~
|
|
||||||
* nature tableau associatif: modélise un tableau associatif (le tableau peut
|
|
||||||
avoir des clés numériques ou chaines --> seules les clés décrites par le
|
|
||||||
schéma sont validées)
|
|
||||||
~~~php
|
~~~php
|
||||||
const ASSOC_SCHEMA = [
|
const ASSOC_SCHEMA = [
|
||||||
KEY => VALUE_SCHEMA,
|
KEY => VALUE_SCHEMA,
|
||||||
...
|
...
|
||||||
"" => "assoc",
|
|
||||||
];
|
];
|
||||||
~~~
|
~~~
|
||||||
la nature "tableau associatif" est du sucre syntaxique pour une valeur
|
forme normalisée:
|
||||||
scalaire de type "?array" dont on précise le schéma
|
|
||||||
~~~php
|
~~~php
|
||||||
// la valeur ci-dessus est strictement équivalent à
|
|
||||||
const ASSOC_SCHEMA = [
|
const ASSOC_SCHEMA = [
|
||||||
"?array",
|
"?array", [$default, $title, ...]
|
||||||
"" => "scalar",
|
"" => "assoc",
|
||||||
"schema" => [
|
"schema" => [
|
||||||
KEY => VALUE_SCHEMA,
|
KEY => VALUE_SCHEMA,
|
||||||
...
|
...
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
~~~
|
~~~
|
||||||
|
* liste (tableau d'éléments du même type)
|
||||||
|
le tableau modélisé peut avoir des clés numériques ou chaines --> on ne
|
||||||
|
modélise ni le type ni la valeur des clés
|
||||||
|
|
||||||
* nature liste: modélise une liste de valeurs du même type (le tableau peut
|
forme courante:
|
||||||
avoir des clés numériques ou chaines --> on ne modélise ni le type ni la
|
~~~php
|
||||||
valeur des clés)
|
const LIST_SCHEMA = [[
|
||||||
|
ITEM_SCHEMA,
|
||||||
|
]];
|
||||||
|
~~~
|
||||||
|
forme normalisée:
|
||||||
~~~php
|
~~~php
|
||||||
const LIST_SCHEMA = [
|
const LIST_SCHEMA = [
|
||||||
"?array", [$default, $title, ...]
|
"?array", [$default, $title, ...]
|
||||||
@ -85,7 +75,8 @@ const SCALAR_SCHEMA = [
|
|||||||
"required" => "la valeur est-elle requise? si oui, elle doit exister",
|
"required" => "la valeur est-elle requise? si oui, elle doit exister",
|
||||||
"nullable" => "si la valeur existe, peut-elle être nulle?",
|
"nullable" => "si la valeur existe, peut-elle être nulle?",
|
||||||
"desc" => "description de la valeur",
|
"desc" => "description de la valeur",
|
||||||
"checker_func" => "une fonction qui vérifie une valeur et la classifie",
|
"analyzer_func" => "XXX",
|
||||||
|
"extractor_func" => "XXX",
|
||||||
"parser_func" => "une fonction qui analyse une chaine pour produire la valeur",
|
"parser_func" => "une fonction qui analyse une chaine pour produire la valeur",
|
||||||
"messages" => "messages à afficher en cas d'erreur d'analyse",
|
"messages" => "messages à afficher en cas d'erreur d'analyse",
|
||||||
"formatter_func" => "une fonction qui formatte la valeur pour affichage",
|
"formatter_func" => "une fonction qui formatte la valeur pour affichage",
|
||||||
@ -118,9 +109,9 @@ nature scalaire si:
|
|||||||
* c'est un tableau avec un élément à l'index 0, ainsi que d'autres éléments,
|
* c'est un tableau avec un élément à l'index 0, ainsi que d'autres éléments,
|
||||||
e.g `["string", null, "required" => true]`
|
e.g `["string", null, "required" => true]`
|
||||||
|
|
||||||
message indique les messages à afficher en cas d'erreur d'analyse. les clés sont
|
`messages` indique les messages à afficher en cas d'erreur d'analyse. les clés
|
||||||
normalisées et correspondent à différents états de la valeur tels qu'analysés
|
sont normalisées et correspondent à différents états de la valeur tels
|
||||||
par `checker_func`
|
qu'analysés par `analyzer_func`
|
||||||
~~~php
|
~~~php
|
||||||
const MESSAGE_SCHEMA = [
|
const MESSAGE_SCHEMA = [
|
||||||
"missing" => "message si la valeur n'existe pas dans la source et qu'elle est requise",
|
"missing" => "message si la valeur n'existe pas dans la source et qu'elle est requise",
|
||||||
@ -133,23 +124,27 @@ const MESSAGE_SCHEMA = [
|
|||||||
|
|
||||||
## Schéma d'un tableau associatif
|
## Schéma d'un tableau associatif
|
||||||
|
|
||||||
Dans sa forme *non normalisée*, un tableau associatif est généralement modélisé
|
Dans la forme courante, on considère que c'est un schéma de nature associative si:
|
||||||
de cette manière:
|
|
||||||
~~~php
|
|
||||||
const ASSOC_SCHEMA = [
|
|
||||||
KEY => VALUE_SCHEMA,
|
|
||||||
...
|
|
||||||
"" => "assoc",
|
|
||||||
];
|
|
||||||
~~~
|
|
||||||
où chaque occurrence de `KEY => VALUE_SCHEMA` définit le schéma de la valeur
|
|
||||||
dont la clé est `KEY`
|
|
||||||
|
|
||||||
Si la nature du schéma n'est pas spécifiée, on considère que c'est un schéma de
|
|
||||||
nature associative si:
|
|
||||||
* c'est un tableau uniquement associatif avec aucun élément séquentiel, e.g
|
* c'est un tableau uniquement associatif avec aucun élément séquentiel, e.g
|
||||||
`["name" => "string", "age" => "int"]`
|
`["name" => "string", "age" => "int"]`
|
||||||
|
|
||||||
|
La forme normalisée est
|
||||||
|
~~~php
|
||||||
|
const ASSOC_SCHEMA = [
|
||||||
|
"?array",
|
||||||
|
"" => "assoc",
|
||||||
|
"schema" => [
|
||||||
|
KEY => VALUE_SCHEMA,
|
||||||
|
...
|
||||||
|
],
|
||||||
|
];
|
||||||
|
~~~
|
||||||
|
le type "?array" ou "array" indique si la liste est nullable ou non. la valeur
|
||||||
|
par défaut est "?array"
|
||||||
|
|
||||||
|
chaque occurrence de `KEY => VALUE_SCHEMA` définit le schéma de la valeur dont
|
||||||
|
la clé est `KEY`
|
||||||
|
|
||||||
VALUE_SCHEMA peut-être n'importe quel schéma valide, qui sera analysé
|
VALUE_SCHEMA peut-être n'importe quel schéma valide, qui sera analysé
|
||||||
récursivement, avec cependant l'ajout de quelques clés supplémentaires:
|
récursivement, avec cependant l'ajout de quelques clés supplémentaires:
|
||||||
* description de la valeur dans le contexte du tableau
|
* description de la valeur dans le contexte du tableau
|
||||||
@ -171,14 +166,11 @@ récursivement, avec cependant l'ajout de quelques clés supplémentaires:
|
|||||||
|
|
||||||
## Schéma d'une liste (tableau séquentiel ou associatif d'éléments du même type)
|
## Schéma d'une liste (tableau séquentiel ou associatif d'éléments du même type)
|
||||||
|
|
||||||
Dans sa forme *non normalisée*, une liste est généralement modélisée de cette
|
Dans la forme courante, on considère que c'est un schéma de nature liste si:
|
||||||
manière:
|
* c'est un tableau avec un unique élément de type tableau à l'index 0, e.g
|
||||||
~~~php
|
`[["string", null, "required" => true]]`
|
||||||
const LIST_SCHEMA = [ITEM_SCHEMA];
|
|
||||||
~~~
|
|
||||||
où ITEM_SCHEMA est le schéma des éléments de la liste
|
|
||||||
|
|
||||||
Pour information, la forme normalisée est plutôt de la forme
|
La forme normalisée est
|
||||||
~~~php
|
~~~php
|
||||||
const LIST_SCHEMA = [
|
const LIST_SCHEMA = [
|
||||||
"?array",
|
"?array",
|
||||||
@ -189,9 +181,4 @@ const LIST_SCHEMA = [
|
|||||||
le type "?array" ou "array" indique si la liste est nullable ou non. la valeur
|
le type "?array" ou "array" indique si la liste est nullable ou non. la valeur
|
||||||
par défaut est "?array"
|
par défaut est "?array"
|
||||||
|
|
||||||
Si la nature du schéma n'est pas spécifiée, on considère que c'est un schéma de
|
|
||||||
nature liste si:
|
|
||||||
* c'est un tableau avec un unique élément de type tableau à l'index 0, e.g
|
|
||||||
`[["string", null, "required" => true]]`
|
|
||||||
|
|
||||||
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
@ -1,30 +1,60 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema;
|
namespace nulib\schema;
|
||||||
|
|
||||||
use nur\sery\wip\schema\_assoc\AssocResult;
|
use IteratorAggregate;
|
||||||
use nur\sery\wip\schema\_list\ListResult;
|
use Throwable;
|
||||||
use nur\sery\wip\schema\_scalar\ScalarResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Result: résultat de l'analyse ou de la normalisation d'une valeur
|
* Class Result: résultat de l'analyse ou de la normalisation d'une valeur
|
||||||
|
*
|
||||||
|
* @property bool $resultAvailable le résultat est-il disponible?
|
||||||
|
* @property bool $present la valeur existe-t-elle?
|
||||||
|
* @property bool $available si la valeur existe, est-elle disponible?
|
||||||
|
* @property bool $null si la valeur est disponible, est-elle nulle?
|
||||||
|
* @property bool $valid si la valeur est disponible, est-elle valide?
|
||||||
|
* @property bool $normalized si la valeur est valide, est-elle normalisée?
|
||||||
|
* @property string|null $messageKey clé de message si la valeur n'est pas valide
|
||||||
|
* @property string|null $message message si la valeur n'est pas valide
|
||||||
|
* @property Throwable|null $exception l'exception qui a fait échouer la
|
||||||
|
* validation le cas échéant
|
||||||
|
* @property string|null $origValue valeur originale avant extraction et analyse
|
||||||
|
* @property mixed|null $normalizedValue la valeur normalisée si elle est
|
||||||
|
* disponible, null sinon. ce champ est utilisé comme optimisation si la valeur
|
||||||
|
* normalisée a déjà été calculée
|
||||||
*/
|
*/
|
||||||
abstract class Result {
|
abstract class Result implements IteratorAggregate {
|
||||||
|
const KEYS = [
|
||||||
|
"resultAvailable",
|
||||||
|
"present", "available", "null", "valid", "normalized",
|
||||||
|
"messageKey", "message", "exception",
|
||||||
|
"origValue", "normalizedValue",
|
||||||
|
];
|
||||||
|
|
||||||
function __construct() {
|
function __construct() {
|
||||||
$this->reset();
|
$this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAssoc(?AssocResult &$assoc=null): bool { return false; }
|
|
||||||
function isList(?ListResult &$list=null): bool { return false; }
|
|
||||||
function isScalar(?ScalarResult &$scalar=null): bool { return false; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
|
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
|
||||||
* objet
|
* objet
|
||||||
*/
|
*/
|
||||||
abstract function getKeys(): array;
|
abstract function getKeys(): array;
|
||||||
|
|
||||||
/** obtenir un objet pour gérer la valeur spécifiée */
|
/**
|
||||||
abstract function getResult($key=null): Result;
|
* sélectionner le résultat associé à la clé spécifiée
|
||||||
|
*
|
||||||
|
* @param string|int|null $key
|
||||||
|
* @return Result $this
|
||||||
|
*/
|
||||||
|
abstract function select($key): Result;
|
||||||
|
|
||||||
|
function getIterator() {
|
||||||
|
foreach ($this->getKeys() as $key) {
|
||||||
|
yield $key => $this->select($key);
|
||||||
|
}
|
||||||
|
$this->select(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** réinitialiser tous les objets résultats accessibles via cet objet */
|
||||||
abstract function reset(): void;
|
abstract function reset(): void;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,48 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema;
|
namespace nulib\schema;
|
||||||
|
|
||||||
use ArrayAccess;
|
use ArrayAccess;
|
||||||
use nur\sery\AccessException;
|
use nulib\AccessException;
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
use nur\sery\wip\schema\_assoc\AssocSchema;
|
use nulib\ref\schema\ref_schema;
|
||||||
use nur\sery\wip\schema\_list\ListSchema;
|
use nulib\ref\schema\ref_types;
|
||||||
use nur\sery\wip\schema\_scalar\ScalarSchema;
|
use nulib\schema\_assoc\AssocSchema;
|
||||||
|
use nulib\schema\_list\ListSchema;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\types\IType;
|
||||||
|
use nulib\schema\types\tarray;
|
||||||
|
use nulib\schema\types\tbool;
|
||||||
|
use nulib\schema\types\tfunc;
|
||||||
|
use nulib\schema\types\tcontent;
|
||||||
|
use nulib\schema\types\tpkey;
|
||||||
|
use nulib\schema\types\trawstring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Schema
|
||||||
|
*
|
||||||
|
* @property-read array|IType $type
|
||||||
|
* @property-read mixed $default
|
||||||
|
* @property-read string|null $title
|
||||||
|
* @property-read bool $required
|
||||||
|
* @property-read bool $nullable
|
||||||
|
* @property-read string|array|null $desc
|
||||||
|
* @property-read callable|null $analyzerFunc
|
||||||
|
* @property-read callable|null $extractorFunc
|
||||||
|
* @property-read callable|null $parserFunc
|
||||||
|
* @property-read callable|null $normalizerFunc
|
||||||
|
* @property-read array|null $messages
|
||||||
|
* @property-read callable|null $formatterFunc
|
||||||
|
* @property-read mixed $format
|
||||||
|
* @property-read array $nature
|
||||||
|
* @property-read array|null $schema
|
||||||
|
* @property-read string|int|null $name
|
||||||
|
* @property-read string|array|null $pkey
|
||||||
|
* @property-read string|null $header
|
||||||
|
* @property-read bool|null $computed
|
||||||
|
*/
|
||||||
abstract class Schema implements ArrayAccess {
|
abstract class Schema implements ArrayAccess {
|
||||||
/**
|
/**
|
||||||
* créer si besoin une nouvelle instance de {@link Schema} à partir d'une
|
* créer le cas échéant une nouvelle instance de {@link Schema} à partir d'une
|
||||||
* définition de schéma
|
* définition de schéma
|
||||||
*
|
*
|
||||||
* - si $schema est une instance de schéma, la retourner
|
* - si $schema est une instance de schéma, la retourner
|
||||||
@ -18,18 +50,18 @@ abstract class Schema implements ArrayAccess {
|
|||||||
* l'instance de Schema nouvelle créée
|
* l'instance de Schema nouvelle créée
|
||||||
* - sinon, prendre $definition comme définition
|
* - sinon, prendre $definition comme définition
|
||||||
*/
|
*/
|
||||||
static function ns(&$schema, $definition=null, $definitionKey=null): self {
|
static function ns($definition=null, $definitionKey=null, &$schema=null, bool $normalize=true): self {
|
||||||
if (is_array($schema)) {
|
if (is_array($schema)) {
|
||||||
$definition = $schema;
|
$definition = $schema;
|
||||||
$schema = null;
|
$schema = null;
|
||||||
}
|
}
|
||||||
if ($schema === null) {
|
if ($schema === null) {
|
||||||
if (AssocSchema::isa_definition($definition)) {
|
if (AssocSchema::isa_definition($definition)) {
|
||||||
$schema = new AssocSchema($definition, $definitionKey);
|
$schema = new AssocSchema($definition, $definitionKey, $normalize);
|
||||||
} elseif (ListSchema::isa_definition($definition)) {
|
} elseif (ListSchema::isa_definition($definition)) {
|
||||||
$schema = new ListSchema($definition, $definitionKey);
|
$schema = new ListSchema($definition, $definitionKey, $normalize);
|
||||||
} elseif (ScalarSchema::isa_definition($definition)) {
|
} elseif (ScalarSchema::isa_definition($definition)) {
|
||||||
$schema = new ScalarSchema($definition, $definitionKey);
|
$schema = new ScalarSchema($definition, $definitionKey, $normalize);
|
||||||
} else {
|
} else {
|
||||||
throw SchemaException::invalid_schema();
|
throw SchemaException::invalid_schema();
|
||||||
}
|
}
|
||||||
@ -38,17 +70,195 @@ abstract class Schema implements ArrayAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Créer si besoin une nouvelle instance de {@link Value} qui référence la
|
* Créer une nouvelle instance de {@link Wrapper} qui référence la
|
||||||
* variable $dest (si $destKey===null) ou $dest[$destKey] si $destKey n'est
|
* variable $value (si $valueKey===null) ou $value[$valueKey] si $valueKey
|
||||||
* pas null
|
* n'est pas null
|
||||||
*/
|
*/
|
||||||
static function nv(?Value &$destv=null, &$dest=null, $destKey=null, &$schema=null, $definition=null): Value {
|
static function nw(&$value=null, $valueKey=null, $definition=null, &$schema=null, ?Wrapper &$wrapper=null): Wrapper {
|
||||||
if ($definition === null) {
|
if ($definition === null) {
|
||||||
# bien que techniquement, $definition peut être null (il s'agit alors du
|
# bien que techniquement, $definition peut être null (il s'agit alors du
|
||||||
# schéma d'un scalaire quelconque), on ne l'autorise pas ici
|
# schéma d'un scalaire quelconque), on ne l'autorise pas ici
|
||||||
throw SchemaException::invalid_schema("definition is required");
|
throw SchemaException::invalid_schema("definition is required");
|
||||||
}
|
}
|
||||||
return self::ns($schema, $definition)->newValue($destv, $dest, $destKey);
|
return self::ns($definition, null, $schema)->getWrapper($value, $valueKey, null, $wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function have_nature(array $definition, ?string &$nature=null): bool {
|
||||||
|
$definitionNature = $definition[""] ?? null;
|
||||||
|
if (is_string($definitionNature)) {
|
||||||
|
$nature = $definitionNature;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (is_array($definitionNature)
|
||||||
|
&& array_key_exists(0, $definitionNature)
|
||||||
|
&& is_string($definitionNature[0])) {
|
||||||
|
$nature = $definitionNature;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _normalize_definition(&$definition, $definitionKey=null, ?array $natureMetaschema=null): void {
|
||||||
|
if (!is_array($definition)) $definition = [$definition];
|
||||||
|
# s'assurer que toutes les clés existent avec leur valeur par défaut
|
||||||
|
$index = 0;
|
||||||
|
foreach (array_keys(ref_schema::VALUE_METASCHEMA) as $key) {
|
||||||
|
if (!array_key_exists($key, $definition)) {
|
||||||
|
if (array_key_exists($index, $definition)) {
|
||||||
|
$definition[$key] = $definition[$index];
|
||||||
|
unset($definition[$index]);
|
||||||
|
$index++;
|
||||||
|
} else {
|
||||||
|
$definition[$key] = ref_schema::VALUE_METASCHEMA[$key][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# réordonner les clés numériques
|
||||||
|
if (cl::have_num_keys($definition)) {
|
||||||
|
$keys = array_keys($definition);
|
||||||
|
$index = 0;
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (!is_int($key)) continue;
|
||||||
|
if ($key !== $index) {
|
||||||
|
$definition[$index] = $definition[$key];
|
||||||
|
unset($definition[$key]);
|
||||||
|
}
|
||||||
|
$index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# type
|
||||||
|
$types = [];
|
||||||
|
$deftype = $definition["type"];
|
||||||
|
$nullable = $definition["nullable"] ?? false;
|
||||||
|
if ($deftype === null) {
|
||||||
|
$types[] = null;
|
||||||
|
$nullable = true;
|
||||||
|
} else {
|
||||||
|
if (!is_array($deftype)) {
|
||||||
|
if (!is_string($deftype)) throw SchemaException::invalid_type($deftype);
|
||||||
|
$deftype = explode("|", $deftype);
|
||||||
|
}
|
||||||
|
foreach ($deftype as $type) {
|
||||||
|
if ($type !== null) $type = trim($type);
|
||||||
|
if ($type === null || $type === "null") {
|
||||||
|
$nullable = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!is_string($type)) throw SchemaException::invalid_type($type);
|
||||||
|
if (substr($type, 0, 1) == "?") {
|
||||||
|
$type = substr($type, 1);
|
||||||
|
$nullable = true;
|
||||||
|
}
|
||||||
|
if ($type === "") throw SchemaException::invalid_type($type);
|
||||||
|
$type = cl::get(ref_types::ALIASES, $type, $type);
|
||||||
|
$types = array_merge($types, explode("|", $type));
|
||||||
|
}
|
||||||
|
if (!$types) throw SchemaException::invalid_schema("scalar: type is required");
|
||||||
|
$types = array_keys(array_fill_keys($types, true));
|
||||||
|
}
|
||||||
|
$definition["type"] = $types;
|
||||||
|
$definition["nullable"] = $nullable;
|
||||||
|
# nature
|
||||||
|
$nature = $definition[""];
|
||||||
|
tarray::ensure_array($nature);
|
||||||
|
$natureMetaschema ??= ref_schema::NATURE_METASCHEMA;
|
||||||
|
foreach (array_keys($natureMetaschema) as $key) {
|
||||||
|
if (!array_key_exists($key, $nature)) {
|
||||||
|
$nature[$key] = $natureMetaschema[$key][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$definition[""] = $nature;
|
||||||
|
# name, pkey, header
|
||||||
|
$name = $definition["name"];
|
||||||
|
$pkey = $definition["pkey"];
|
||||||
|
$header = $definition["header"];
|
||||||
|
if ($name === null) $name = $definitionKey;
|
||||||
|
trawstring::ensure_nstring($name);
|
||||||
|
tpkey::ensure_npkey($pkey);
|
||||||
|
trawstring::ensure_nstring($header);
|
||||||
|
if ($pkey === null) $pkey = $name;
|
||||||
|
if ($header === null) $header = $name;
|
||||||
|
$definition["name"] = $name;
|
||||||
|
$definition["pkey"] = $pkey;
|
||||||
|
$definition["header"] = $header;
|
||||||
|
# autres éléments
|
||||||
|
tarray::ensure_narray($definition["schema"]);
|
||||||
|
trawstring::ensure_nstring($definition["title"]);
|
||||||
|
tbool::ensure_bool($definition["required"]);
|
||||||
|
tbool::ensure_bool($definition["nullable"]);
|
||||||
|
tcontent::ensure_ncontent($definition["desc"]);
|
||||||
|
tfunc::ensure_nfunc($definition["analyzer_func"]);
|
||||||
|
tfunc::ensure_nfunc($definition["extractor_func"]);
|
||||||
|
tfunc::ensure_nfunc($definition["parser_func"]);
|
||||||
|
tfunc::ensure_nfunc($definition["normalizer_func"]);
|
||||||
|
tarray::ensure_narray($definition["messages"]);
|
||||||
|
tfunc::ensure_nfunc($definition["formatter_func"]);
|
||||||
|
tbool::ensure_nbool($definition["computed"]);
|
||||||
|
|
||||||
|
switch ($nature[0] ?? null) {
|
||||||
|
case "assoc":
|
||||||
|
foreach ($definition["schema"] as $key => &$keydef) {
|
||||||
|
self::_normalize_definition($keydef, $key);
|
||||||
|
}; unset($keydef);
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
self::_normalize_definition($definition["schema"]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _ensure_nature(array $definition, string $expectedNature, ?string $expectedType=null): void {
|
||||||
|
$nature = $definition[""];
|
||||||
|
if (!array_key_exists(0, $nature) || $nature[0] !== $expectedNature) {
|
||||||
|
throw SchemaException::invalid_schema("$nature: invalid nature. expected $expectedNature");
|
||||||
|
}
|
||||||
|
if ($expectedType !== null) {
|
||||||
|
$types = $definition["type"];
|
||||||
|
if (count($types) !== 1 || $types[0] !== $expectedType) {
|
||||||
|
throw new SchemaException("{$types[O]}: invalide type. expected $expectedType");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _ensure_type(array &$definition): void {
|
||||||
|
$types = $definition["type"];
|
||||||
|
$nullable = $definition["nullable"];
|
||||||
|
# s'il n'y a qu'une seul type, l'instancier tout de suite
|
||||||
|
if (is_array($types) && count($types) == 1 && $types[0] !== null) {
|
||||||
|
foreach ($types as $key => $name) {
|
||||||
|
if ($key === 0) {
|
||||||
|
$args = null;
|
||||||
|
} else {
|
||||||
|
$args = $name;
|
||||||
|
$name = $key;
|
||||||
|
}
|
||||||
|
$definition["type"] = types::get($nullable, $name, $args, $definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch ($definition[""][0]) {
|
||||||
|
case "assoc":
|
||||||
|
foreach ($definition["schema"] as &$keydef) {
|
||||||
|
self::_ensure_type($keydef);
|
||||||
|
}; unset($keydef);
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
self::_ensure_type($definition["schema"]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function _ensure_schema_instances(array &$definition): void {
|
||||||
|
switch ($definition[""][0]) {
|
||||||
|
case "assoc":
|
||||||
|
foreach ($definition["schema"] as &$keydef) {
|
||||||
|
self::_ensure_schema_instances($keydef);
|
||||||
|
Schema::ns(null, null, $keydef, false);
|
||||||
|
}; unset($keydef);
|
||||||
|
break;
|
||||||
|
case "list":
|
||||||
|
Schema::ns(null, null, $definition["schema"], false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,17 +267,24 @@ abstract class Schema implements ArrayAccess {
|
|||||||
*/
|
*/
|
||||||
const SCHEMA = null;
|
const SCHEMA = null;
|
||||||
|
|
||||||
/** @var array */
|
protected array $_definition;
|
||||||
protected $definition;
|
|
||||||
|
|
||||||
/** retourner true si le schéma est de nature tableau associatif */
|
protected array $definition;
|
||||||
function isAssoc(?AssocSchema &$assoc=null): bool { return false; }
|
|
||||||
/** retourner true si le schéma est de nature liste */
|
|
||||||
function isList(?ListSchema &$list=null): bool { return false; }
|
|
||||||
/** retourner true si le schéma est de nature scalaire */
|
|
||||||
function isScalar(?ScalarSchema &$scalar=null): bool { return false; }
|
|
||||||
|
|
||||||
abstract function newValue(?Value &$destv=null, &$dest=null, $destKey=null): Value;
|
function getDefinition(): array {
|
||||||
|
return $this->_definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retourner la liste des clés valides pour l'accès aux valeurs et résultats
|
||||||
|
*/
|
||||||
|
abstract function getKeys(): array;
|
||||||
|
|
||||||
|
abstract function getSchema($key=false): Schema;
|
||||||
|
|
||||||
|
abstract protected function newWrapper(): Wrapper;
|
||||||
|
|
||||||
|
abstract function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): Wrapper;
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
# key & properties
|
# key & properties
|
||||||
@ -86,7 +303,15 @@ abstract class Schema implements ArrayAccess {
|
|||||||
throw AccessException::read_only(null, $offset);
|
throw AccessException::read_only(null, $offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
const _PROPERTY_PKEYS = [];
|
const _PROPERTY_PKEYS = [
|
||||||
|
"analyzerFunc" => "analyzer_func",
|
||||||
|
"extractorFunc" => "extractor_func",
|
||||||
|
"parserFunc" => "parser_func",
|
||||||
|
"normalizerFunc" => "normalizer_func",
|
||||||
|
"formatterFunc" => "formatter_func",
|
||||||
|
"nature" => ["", 0],
|
||||||
|
];
|
||||||
|
|
||||||
function __get($name) {
|
function __get($name) {
|
||||||
$pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name);
|
$pkey = cl::get(static::_PROPERTY_PKEYS, $name, $name);
|
||||||
return cl::pget($this->definition, $pkey);
|
return cl::pget($this->definition, $pkey);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema;
|
namespace nulib\schema;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
|
@ -1,19 +1,50 @@
|
|||||||
# nur\sery\schema
|
# nulib\schema
|
||||||
|
|
||||||
|
* l'ordre est `ensureAssoc [--> ensureKeys] [--> orderKeys]`
|
||||||
|
* si false, supprimer la clé du tableau sauf si ensureKeys
|
||||||
|
|
||||||
|
* pour AssocResult, les clés suivantes sont supportées:
|
||||||
|
* false pour la clé courante
|
||||||
|
* null pour un résultat aggrégé
|
||||||
|
* "" pour le résultat du tableau
|
||||||
|
* $key pour le résultat de la clé correspondante
|
||||||
|
|
||||||
|
* rajouter l'attribut "size" pour spécifier la taille maximale des valeurs
|
||||||
|
* cela pourrait servir pour générer automatiquement des tables SQL
|
||||||
|
* ou pour modéliser un schéma FSV
|
||||||
|
|
||||||
|
* support allowed_values
|
||||||
|
|
||||||
|
* valeurs composite/computed
|
||||||
|
* analyse / vérification de la valeur complète après calcul du résultat, si
|
||||||
|
tous les résultats sont bons
|
||||||
|
* calcul des valeurs composites/computed par une fonction avant/après l'analyse
|
||||||
|
globale si résultat ok
|
||||||
|
* fonction getter_func, setter_func, deleter_func pour les propriétés de type
|
||||||
|
computed
|
||||||
|
* tdate et tdatetime. qu'en est-il des autres classes (delay, etc.)
|
||||||
|
* parse_format pour spécifier le format d'analyse au lieu de l'auto-détecter
|
||||||
|
|
||||||
|
* ScalarSchema::from_property()
|
||||||
|
|
||||||
|
* l'argument $format de AssocWrapper::format() est un tableau associatif
|
||||||
|
`[$key => $format]`
|
||||||
|
cela permet de spécifier des format spécifiques pour certains champs.
|
||||||
|
* cela signifie que la valeur de retour n'est pas string :-(
|
||||||
|
retourner string|array
|
||||||
|
|
||||||
* implémenter support `analyzer_func`, `extractor_func`, `parser_func`,
|
|
||||||
`normalizer_func`, `formatter_func`
|
|
||||||
* dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe
|
* dans AssocSchema, support `[key_prefix]` qui permet de spécifier un préfixe
|
||||||
commun aux champs dans le tableau destination, e.g
|
commun aux champs dans le tableau destination, e.g
|
||||||
~~~php
|
~~~php
|
||||||
$value = Schema::ns($schema, [
|
$wrapper = Schema::ns($schema, [
|
||||||
"a" => "?string",
|
"a" => "?string",
|
||||||
"b" => "?int",
|
"b" => "?int",
|
||||||
])->newValue();
|
])->newWrapper();
|
||||||
$dest = ["x_a" => 5, "x_b" => "10"],
|
$value = ["x_a" => 5, "x_b" => "10"],
|
||||||
$value->reset($dest, null, [
|
$wrapper->reset($value, null, [
|
||||||
"key_prefix" => "x_",
|
"key_prefix" => "x_",
|
||||||
]);
|
]);
|
||||||
# $dest vaut ["x_a" => "5", "x_b" => 10];
|
# $value vaut ["x_a" => "5", "x_b" => 10];
|
||||||
~~~
|
~~~
|
||||||
définir si le préfixe doit être spécifié sur le schéma ou sur la valeur...
|
définir si le préfixe doit être spécifié sur le schéma ou sur la valeur...
|
||||||
actuellement, le code ne permet pas de définir de tels paramètres...
|
actuellement, le code ne permet pas de définir de tels paramètres...
|
||||||
@ -21,48 +52,16 @@
|
|||||||
alternative: c'est lors de la *définition* du schéma que le préfixe est ajouté
|
alternative: c'est lors de la *définition* du schéma que le préfixe est ajouté
|
||||||
e.g
|
e.g
|
||||||
~~~php
|
~~~php
|
||||||
$value = Schema::ns($schema, [
|
$wrapper = Schema::ns($schema, [
|
||||||
"a" => "?string",
|
"a" => "?string",
|
||||||
"b" => "?int",
|
"b" => "?int",
|
||||||
], [
|
], [
|
||||||
"key_prefix" => "x_",
|
"key_prefix" => "x_",
|
||||||
])->newValue();
|
])->newWrapper();
|
||||||
$dest = ["x_a" => 5, "x_b" => "10"],
|
$value = ["x_a" => 5, "x_b" => "10"],
|
||||||
$value->reset($dest);
|
$wrapper->reset($value);
|
||||||
# $dest vaut ["x_a" => "5", "x_b" => 10];
|
# $value vaut ["x_a" => "5", "x_b" => 10];
|
||||||
~~~
|
~~~
|
||||||
* dans la définition, `[type]` est remplacé par l'instance de IType lors de sa
|
|
||||||
résolution?
|
|
||||||
* implémenter l'instanciation de types avec des paramètres particuliers. *si*
|
|
||||||
des paramètres sont fournis, le type est instancié avec la signature
|
|
||||||
`IType($typeDefinition, $schemaDefinition)` e.g
|
|
||||||
~~~php
|
|
||||||
const SCHEMA = ["type", default, "required" => true];
|
|
||||||
# le type est instancié comme suit:
|
|
||||||
$type = new ttype();
|
|
||||||
|
|
||||||
const SCHEMA = [[["type", ...]], default, "required" => true];
|
|
||||||
# le type est instancié comme suit:
|
|
||||||
# le tableau peut être une liste ou associatif, c'est au type de décider ce
|
|
||||||
# qu'il en fait
|
|
||||||
$type = new ttype(["type", ...], SCHEMA);
|
|
||||||
~~~
|
|
||||||
* ajouter à IType les méthodes getName() pour le nom officiel du type,
|
|
||||||
getAliases() pour les alias supportés, et getClass() pour la définition de la
|
|
||||||
classe dans les méthodes et propriétés
|
|
||||||
getName() et getAliases() sont juste pour information, ils ne sont pas utilisés
|
|
||||||
lors de la résolution du type effectif.
|
|
||||||
* si cela a du sens, dans AssocSchema, n'instancier les schémas de chaque clé qu'à la demande.
|
|
||||||
l'idée est de ne pas perdre du temps à instancier un schéma qui ne serait pas utilisé
|
|
||||||
|
|
||||||
on pourrait avoir d'une manière générale quelque chose comme:
|
|
||||||
~~~
|
|
||||||
Schema::ensure(&$schema, ?array $def=null, $defKey=null): Schema;
|
|
||||||
~~~
|
|
||||||
* si $schema est une instance de Schema, la retourner
|
|
||||||
* si c'est un array, c'est une définition et il faut la remplacer par l'instance de Schema correspondant
|
|
||||||
* sinon, prendre $def comme définition
|
|
||||||
$key est la clé si $schema est dans un autre schema
|
|
||||||
* actuellement, pour un schéma associatif, si on normalise un tableau séquentiel,
|
* actuellement, pour un schéma associatif, si on normalise un tableau séquentiel,
|
||||||
chaque valeur correspond à la clé de même rang, eg. pour un schéma
|
chaque valeur correspond à la clé de même rang, eg. pour un schéma
|
||||||
~~~php
|
~~~php
|
||||||
@ -77,6 +76,8 @@
|
|||||||
|
|
||||||
la définition de ces "circonstances" est encore à faire: soit un paramètre
|
la définition de ces "circonstances" est encore à faire: soit un paramètre
|
||||||
lors de la définition du schéma, soit un truc magique du genre "toutes les
|
lors de la définition du schéma, soit un truc magique du genre "toutes les
|
||||||
valeurs séquentielles sont des clés du schéma"
|
valeurs séquentielles sont des clés du schéma", soit un mode automatique
|
||||||
|
activé par un paramètre où une valeur "val" devient "val"=>true si la clé
|
||||||
|
"val" existe dans le schéma
|
||||||
|
|
||||||
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\wip\schema;
|
|
||||||
|
|
||||||
use ArrayAccess;
|
|
||||||
use IteratorAggregate;
|
|
||||||
use nur\sery\wip\schema\_assoc\AssocValue;
|
|
||||||
use nur\sery\wip\schema\_list\ListValue;
|
|
||||||
use nur\sery\wip\schema\_scalar\ScalarValue;
|
|
||||||
use nur\sery\wip\schema\types\IType;
|
|
||||||
|
|
||||||
abstract class Value implements ArrayAccess, IteratorAggregate {
|
|
||||||
function isAssoc(?AssocValue &$assoc=null): bool { return false; }
|
|
||||||
function isList(?ListValue &$list=null): bool { return false; }
|
|
||||||
function isScalar(?ScalarValue &$scalar=null): bool { return false; }
|
|
||||||
|
|
||||||
/** spécifier la valeur destination gérée par cet objet */
|
|
||||||
abstract function reset(&$dest, $destKey=null, ?bool $verifix=null): self;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
|
|
||||||
* objet
|
|
||||||
*/
|
|
||||||
abstract function getKeys(): array;
|
|
||||||
|
|
||||||
/** obtenir un objet pour gérer la valeur spécifiée */
|
|
||||||
abstract function getValue($key=null): Value;
|
|
||||||
|
|
||||||
function getIterator() {
|
|
||||||
foreach ($this->getKeys() as $key) {
|
|
||||||
yield $key => $this->getValue($key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* obtenir le résultat de l'appel d'une des fonctions {@link set()} ou
|
|
||||||
* {@link unset()}
|
|
||||||
*/
|
|
||||||
abstract function getResult(): Result;
|
|
||||||
|
|
||||||
/** retourner true si la valeur existe */
|
|
||||||
abstract function isPresent(): bool;
|
|
||||||
|
|
||||||
/** retourner le type associé à la valeur */
|
|
||||||
abstract function getType(): IType;
|
|
||||||
|
|
||||||
/** retourner true si la valeur est disponible */
|
|
||||||
abstract function isAvailable(): bool;
|
|
||||||
|
|
||||||
/** retourner true si la valeur est valide */
|
|
||||||
abstract function isValid(): bool;
|
|
||||||
|
|
||||||
/** retourner true si la valeur est dans sa forme normalisée */
|
|
||||||
abstract function isNormalized(): bool;
|
|
||||||
|
|
||||||
/** obtenir la valeur */
|
|
||||||
abstract function get($default=null);
|
|
||||||
|
|
||||||
/** remplacer la valeur */
|
|
||||||
abstract function set($value): self;
|
|
||||||
|
|
||||||
/** supprimer la valeur */
|
|
||||||
abstract function unset(): self;
|
|
||||||
|
|
||||||
/** formatter la valeur pour affichage */
|
|
||||||
abstract function format($format=null): string;
|
|
||||||
|
|
||||||
#############################################################################
|
|
||||||
# key & properties
|
|
||||||
|
|
||||||
function offsetExists($offset): bool {
|
|
||||||
return in_array($offset, $this->getKeys());
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetGet($offset) {
|
|
||||||
return $this->getValue($offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetSet($offset, $value): void {
|
|
||||||
$this->getValue($offset)->set($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetUnset($offset): void {
|
|
||||||
$this->getValue($offset)->unset();
|
|
||||||
}
|
|
||||||
}
|
|
221
src/schema/Wrapper.php
Normal file
221
src/schema/Wrapper.php
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use IteratorAggregate;
|
||||||
|
use nulib\php\func;
|
||||||
|
use nulib\schema\_assoc\AssocWrapper;
|
||||||
|
use nulib\schema\_list\ListWrapper;
|
||||||
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarWrapper;
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
use nulib\schema\types\IType;
|
||||||
|
|
||||||
|
abstract class Wrapper implements ArrayAccess, IteratorAggregate {
|
||||||
|
protected WrapperContext $context;
|
||||||
|
|
||||||
|
/** changer les paramètres de gestion des valeurs */
|
||||||
|
function resetParams(?array $params): void {
|
||||||
|
$this->context->resetParams($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function resetContext($resetSelectedKey): void {
|
||||||
|
$context = $this->context;
|
||||||
|
$type = $context->schema->type;
|
||||||
|
if (is_array($type)) $type = $type[0];
|
||||||
|
if (is_string($type)) $type = types::get($context->schema->nullable, $type);
|
||||||
|
$context->type = $type;
|
||||||
|
$context->result->reset();
|
||||||
|
$context->analyzed = false;
|
||||||
|
$context->normalized = false;
|
||||||
|
if ($resetSelectedKey) $context->selectedKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function afterModify(?array $params, $resetSelectedKey=false): void {
|
||||||
|
$context = $this->context;
|
||||||
|
$this->resetContext($resetSelectedKey);
|
||||||
|
if ($params["analyze"] ?? $context->analyze) {
|
||||||
|
$this->analyze($params);
|
||||||
|
}
|
||||||
|
if ($context->analyzed) {
|
||||||
|
if ($params["normalize"] ?? $context->normalize) {
|
||||||
|
$this->normalize($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newInput(&$value): Input {
|
||||||
|
return new Input($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spécifier la valeur destination gérée par cet objet.
|
||||||
|
*
|
||||||
|
* @param ?array $params paramètres spécifique à cet appel, qui peuvent être
|
||||||
|
* différent des paramètres par défaut
|
||||||
|
*/
|
||||||
|
function reset(&$value, $valueKey=null, ?array $params=null): Wrapper {
|
||||||
|
$context = $this->context;
|
||||||
|
if ($value instanceof Input) $input = $value;
|
||||||
|
else $input = $this->newInput($value);
|
||||||
|
$context->input = $input;
|
||||||
|
$context->valueKey = $valueKey;
|
||||||
|
$this->afterModify($params, true);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** analyser la valeur */
|
||||||
|
abstract static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int;
|
||||||
|
|
||||||
|
function analyze(?array $params=null): bool {
|
||||||
|
$context = $this->context;
|
||||||
|
$reanalyze = $params["reanalyze"] ?? false;
|
||||||
|
if ($context->analyzed && !$reanalyze) return false;
|
||||||
|
|
||||||
|
static::_analyze($context, $this, $params);
|
||||||
|
$context->analyzed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** normaliser la valeur */
|
||||||
|
abstract static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool;
|
||||||
|
|
||||||
|
function normalize(?array $params=null): bool {
|
||||||
|
$context = $this->context;
|
||||||
|
|
||||||
|
// il faut que la valeur soit analysée avant de la normaliser
|
||||||
|
static::analyze($params);
|
||||||
|
if (!$context->analyzed) return false;
|
||||||
|
|
||||||
|
$renormalize = $params["renormalize"] ?? false;
|
||||||
|
if ($renormalize || !$context->normalized) {
|
||||||
|
$modified = static::_normalize($context, $this, $params);
|
||||||
|
$context->normalized = true;
|
||||||
|
} else {
|
||||||
|
$modified = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ScalarResult $result */
|
||||||
|
$result = $context->result;
|
||||||
|
if (!$result->valid) {
|
||||||
|
$result->throw($params["throw"] ?? $context->throw);
|
||||||
|
}
|
||||||
|
return $modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtenir la liste des clés valides pour les valeurs accessibles via cet
|
||||||
|
* objet
|
||||||
|
*/
|
||||||
|
abstract function getKeys(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sélectionner le wrapper associé à la clé spécifiée
|
||||||
|
*
|
||||||
|
* @param string|int|null $key
|
||||||
|
* @return Wrapper $this
|
||||||
|
*/
|
||||||
|
abstract function select($key): Wrapper;
|
||||||
|
|
||||||
|
function getIterator() {
|
||||||
|
foreach ($this->getKeys() as $key) {
|
||||||
|
yield $key => $this->select($key);
|
||||||
|
}
|
||||||
|
$this->select(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* obtenir le résultat de l'analyse de la valeur du wrapper sélectionné
|
||||||
|
*
|
||||||
|
* cette fonction doit être appelée après {@link set()} ou {@link unset()} et
|
||||||
|
* après que le wrapper aie été sélectionné avec {@link select()}
|
||||||
|
*/
|
||||||
|
function getResult($key=false): Result {
|
||||||
|
return $this->context->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** retourner true si la valeur existe */
|
||||||
|
function isPresent($key=false): bool {
|
||||||
|
return $this->getResult($key)->present;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** retourner le type associé à la valeur */
|
||||||
|
function getType($key=false): IType {
|
||||||
|
return $this->context->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** retourner true si la valeur est disponible */
|
||||||
|
function isAvailable($key=false): bool {
|
||||||
|
return $this->getResult($key)->available;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** retourner true si la valeur est valide */
|
||||||
|
function isValid($key=false): bool {
|
||||||
|
return $this->getResult($key)->valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** retourner true si la valeur est dans sa forme normalisée */
|
||||||
|
function isNormalized($key=false): bool {
|
||||||
|
return $this->getResult($key)->normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($default=null, $key=false) {
|
||||||
|
$context = $this->context;
|
||||||
|
if (!$context->result->available) return $default;
|
||||||
|
return $context->input->get($context->valueKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set($value, ?array $params=null, $key=false): self {
|
||||||
|
$context = $this->context;
|
||||||
|
$context->input->set($value, $context->valueKey);
|
||||||
|
$this->afterModify($params);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unset(?array $params=null, $key=false): self {
|
||||||
|
$context = $this->context;
|
||||||
|
$context->input->unset($context->valueKey);
|
||||||
|
$this->afterModify($params);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _format(WrapperContext $context, $format=null): string {
|
||||||
|
$value = $context->input->get($context->valueKey);
|
||||||
|
/** @var func $formatterFunc */
|
||||||
|
$formatterFunc = $context->schema->formatterFunc;
|
||||||
|
if ($formatterFunc !== null) {
|
||||||
|
# la fonction formatter n'a pas forcément accès au format de la définition
|
||||||
|
# le lui fournir ici
|
||||||
|
$format ??= $context->schema->format;
|
||||||
|
return $formatterFunc->invoke([$value, $format, $context, $this]);
|
||||||
|
} else {
|
||||||
|
# on assume que le type a été initialisé avec le format de la définition
|
||||||
|
# le cas échéant
|
||||||
|
return $context->type->format($value, $format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** formatter la valeur pour affichage */
|
||||||
|
function format($format=null, $key=false): string {
|
||||||
|
return $this->_format($this->context, $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# key & properties
|
||||||
|
|
||||||
|
function offsetExists($offset): bool {
|
||||||
|
return in_array($offset, $this->getKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetGet($offset) {
|
||||||
|
return $this->get(null, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetSet($offset, $value): void {
|
||||||
|
$this->set($value, null, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetUnset($offset): void {
|
||||||
|
$this->unset(null, $offset);
|
||||||
|
}
|
||||||
|
}
|
46
src/schema/WrapperContext.php
Normal file
46
src/schema/WrapperContext.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema;
|
||||||
|
|
||||||
|
use nulib\ref\schema\ref_schema;
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
use nulib\schema\types\IType;
|
||||||
|
|
||||||
|
class WrapperContext {
|
||||||
|
|
||||||
|
function __construct(Schema $schema, ?Input $input, $valueKey, ?array $params) {
|
||||||
|
$this->resetParams($params);
|
||||||
|
$this->schema = $schema;
|
||||||
|
if ($input !== null) $this->input = $input;
|
||||||
|
$this->valueKey = $valueKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ?array $params;
|
||||||
|
public bool $analyze, $analyzed = false;
|
||||||
|
public bool $normalize, $normalized = false;
|
||||||
|
public ?bool $throw;
|
||||||
|
|
||||||
|
function resetParams(?array $params): void {
|
||||||
|
$this->params = $params;
|
||||||
|
$this->analyze = $params["analyze"] ?? ref_schema::PARAMS_SCHEMA["analyze"][1];
|
||||||
|
$this->normalize = $params["normalize"] ?? ref_schema::PARAMS_SCHEMA["normalize"][1];
|
||||||
|
$this->throw = $params["throw"] ?? ref_schema::PARAMS_SCHEMA["throw"][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** schéma de la valeur */
|
||||||
|
public Schema $schema;
|
||||||
|
/** source et destination de la valeur */
|
||||||
|
public Input $input;
|
||||||
|
/** @var string|int|null clé de la valeur dans le tableau destination */
|
||||||
|
public $valueKey;
|
||||||
|
/** @var mixed */
|
||||||
|
public $origValue = null;
|
||||||
|
/** @var mixed */
|
||||||
|
public $value = null;
|
||||||
|
|
||||||
|
/** @var string|int|null clé sélectionnée */
|
||||||
|
public $selectedKey = null;
|
||||||
|
/** type de la valeur de la clé sélectionnée après analyse */
|
||||||
|
public ?IType $type = null;
|
||||||
|
/** résultat de l'analyse de la valeur de la clé sélectionnée */
|
||||||
|
public ?Result $result = null;
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\wip\schema\_assoc;
|
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
|
||||||
|
|
||||||
class AssocResult extends Result {
|
|
||||||
function isAssoc(?AssocResult &$assoc=null): bool { $assoc = $this; return true;}
|
|
||||||
}
|
|
@ -1,55 +1,96 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\_assoc;
|
namespace nulib\schema\_assoc;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
use nur\sery\ref\schema\ref_schema;
|
use nulib\ref\schema\ref_schema;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\Value;
|
use nulib\schema\Schema;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AssocSchema
|
* Class AssocSchema
|
||||||
*/
|
*/
|
||||||
class AssocSchema extends Schema {
|
class AssocSchema extends Schema {
|
||||||
/** @var array meta-schema d'un schéma de nature tableau associatif */
|
|
||||||
const METASCHEMA = ref_schema::ASSOC_METASCHEMA;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* indiquer si $definition est une définition de schéma de nature tableau
|
* indiquer si $definition est une définition de schéma de nature tableau
|
||||||
* associatif que {@link normalize()} pourrait normaliser
|
* associatif que {@link normalize_definition()} pourrait normaliser
|
||||||
*/
|
*/
|
||||||
static function isa_definition($definition): bool {
|
static function isa_definition($definition): bool {
|
||||||
if (!is_array($definition)) return false;
|
if (!is_array($definition)) return false;
|
||||||
# nature explicitement spécifiée
|
# nature explicitement spécifiée
|
||||||
if (array_key_exists("", $definition)) {
|
if (self::have_nature($definition, $nature)) {
|
||||||
$nature = $definition[""];
|
return $nature === "assoc";
|
||||||
if ($nature === "assoc") return true;
|
|
||||||
if (is_array($nature)
|
|
||||||
&& array_key_exists(0, $nature)
|
|
||||||
&& $nature[0] === "assoc") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
# un tableau associatif
|
# tableau associatif
|
||||||
return !cl::have_num_keys($definition);
|
return !cl::have_num_keys($definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function normalize($definition, $definitionKey=null): array {
|
static function normalize_definition($definition, $definitionKey=null): array {
|
||||||
|
if (!is_array($definition)) $definition = [$definition];
|
||||||
|
if (!self::have_nature($definition)) {
|
||||||
|
$definition = [
|
||||||
|
"?array",
|
||||||
|
"" => "assoc",
|
||||||
|
"schema" => $definition,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::ASSOC_NATURE_METASCHEMA);
|
||||||
|
self::_normalize_definition($definition, $definitionKey, $natureMetaschema);
|
||||||
|
self::_ensure_nature($definition, "assoc", "array");
|
||||||
|
return $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
|
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
|
||||||
if ($definition === null) $definition = static::SCHEMA;
|
if ($definition === null) $definition = static::SCHEMA;
|
||||||
if ($normalize) $definition = self::normalize($definition, $definitionKey);
|
if ($normalize) {
|
||||||
|
$definition = self::normalize_definition($definition, $definitionKey);
|
||||||
|
$this->_definition = $definition;
|
||||||
|
self::_ensure_type($definition);
|
||||||
|
self::_ensure_schema_instances($definition);
|
||||||
|
} else {
|
||||||
|
# ici, $definition contient un schema déjà instancié, mais c'est le mieux
|
||||||
|
# qu'on puisse faire
|
||||||
|
$this->_definition = $definition;
|
||||||
|
}
|
||||||
$this->definition = $definition;
|
$this->definition = $definition;
|
||||||
|
$keys = [];
|
||||||
|
foreach ($definition["schema"] as $key => $schema) {
|
||||||
|
if (!$schema["computed"]) $keys[] = $key;
|
||||||
|
}
|
||||||
|
$this->keys = $keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAssoc(?AssocSchema &$assoc=null): bool {
|
protected array $keys;
|
||||||
$assoc = $this;
|
|
||||||
return true;
|
function getKeys(): array {
|
||||||
|
return $this->keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newValue(?Value &$destv=null, &$dest=null, $destKey=null): AssocValue {
|
function getSchema($key=false): Schema {
|
||||||
if ($destv instanceof AssocValue) return $destv->reset($dest, $destKey);
|
if ($key === null || $key === false) return $this;
|
||||||
else return ($destv = new AssocValue($this, $dest, $destKey));
|
$schema = $this->definition["schema"][$key] ?? null;
|
||||||
|
if ($schema === null) throw ValueException::invalid_key($key);
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newWrapper(): AssocWrapper {
|
||||||
|
return new AssocWrapper($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): AssocWrapper {
|
||||||
|
# si pas de valeur ni de wrapper, pas d'analyse et donc pas d'exception
|
||||||
|
# cf le code similaire dans AssocWrapper::__construct()
|
||||||
|
$dontAnalyze = $value === null && $wrapper === null;
|
||||||
|
if (!($wrapper instanceof AssocWrapper)) $wrapper = $this->newWrapper();
|
||||||
|
|
||||||
|
# la nature du schéma peut contenir des paramètres par défaut
|
||||||
|
$nature = $this->definition[""];
|
||||||
|
foreach (array_keys(ref_schema::ASSOC_PARAMS_SCHEMA) as $paramKey) {
|
||||||
|
$paramValue = $nature[$paramKey] ?? null;
|
||||||
|
if ($paramValue !== null) $params[$paramKey] = $paramValue;
|
||||||
|
}
|
||||||
|
if ($params !== null) $wrapper->resetParams($params);
|
||||||
|
|
||||||
|
return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\wip\schema\_assoc;
|
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
|
||||||
use nur\sery\wip\schema\Value;
|
|
||||||
|
|
||||||
class AssocValue extends Value {
|
|
||||||
function isAssoc(?AssocValue &$assoc=null): bool { $assoc = $this; return true; }
|
|
||||||
|
|
||||||
function ensureKeys(): bool {
|
|
||||||
}
|
|
||||||
function orderKeys(): bool {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param Result[] $results */
|
|
||||||
function verifix(bool $throw=true, ?array &$results=null): bool {
|
|
||||||
}
|
|
||||||
}
|
|
185
src/schema/_assoc/AssocWrapper.php
Normal file
185
src/schema/_assoc/AssocWrapper.php
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\_assoc;
|
||||||
|
|
||||||
|
use nulib\cl;
|
||||||
|
use nulib\ref\schema\ref_analyze;
|
||||||
|
use nulib\ValueException;
|
||||||
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarWrapper;
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\types\IType;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
use nulib\schema\WrapperContext;
|
||||||
|
|
||||||
|
class AssocWrapper extends Wrapper {
|
||||||
|
function __construct(AssocSchema $schema, &$value=null, $valueKey=null, ?array $params=null) {
|
||||||
|
$keys = $schema->getKeys();
|
||||||
|
$keyParams = cl::merge($params, [
|
||||||
|
"throw" => false,
|
||||||
|
]);
|
||||||
|
$keyWrappers = [];
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$value = null;
|
||||||
|
$keyWrappers[$key] = $schema->getSchema($key)->getWrapper($value, null, $keyParams);
|
||||||
|
}
|
||||||
|
$this->context = $context = new AssocWrapperContext($schema, null, null, $params);
|
||||||
|
$arrayParams = cl::merge($params, [
|
||||||
|
"throw" => false,
|
||||||
|
]);
|
||||||
|
$context->arrayWrapper = new ScalarWrapper($schema, $dummy, null, $arrayParams, $context);
|
||||||
|
$context->keys = $keys;
|
||||||
|
$context->keyWrappers = $keyWrappers;
|
||||||
|
|
||||||
|
if ($value !== null) {
|
||||||
|
# n'initialiser que si $value n'est pas null
|
||||||
|
$this->reset($value, $valueKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var AssocWrapperContext */
|
||||||
|
protected WrapperContext $context;
|
||||||
|
|
||||||
|
protected function resetContext($resetSelectedKey): void {
|
||||||
|
parent::resetContext($resetSelectedKey);
|
||||||
|
$context = $this->context;
|
||||||
|
$context->arrayWrapper->getResult()->reset();
|
||||||
|
foreach ($context->keyWrappers as $wrapper) {
|
||||||
|
$wrapper->getResult()->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(&$value, $valueKey=null, ?array $params=null): Wrapper {
|
||||||
|
$context = $this->context;
|
||||||
|
if ($value instanceof Input) $input = $value;
|
||||||
|
else $input = $this->newInput($value);
|
||||||
|
$context->input = $input;
|
||||||
|
$context->valueKey = $valueKey;
|
||||||
|
foreach ($context->keyWrappers as $key => $keyWrapper) {
|
||||||
|
$keyInput = $input->addKey($valueKey);
|
||||||
|
$keyWrapper->reset($keyInput, $key, ["analyze" => false]);
|
||||||
|
}
|
||||||
|
$this->afterModify($params, true);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKeys(): array {
|
||||||
|
return $this->context->keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _getWrapper($key): Wrapper {
|
||||||
|
$context = $this->context;
|
||||||
|
if ($key === null) return $context->arrayWrapper;
|
||||||
|
$wrapper = $context->keyWrappers[$key] ?? null;
|
||||||
|
if ($wrapper === null) throw ValueException::invalid_key($key);
|
||||||
|
return $wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param string|int|null $key */
|
||||||
|
function select($key=null): Wrapper {
|
||||||
|
$wrapper = $this->_getWrapper($key);
|
||||||
|
$this->context->selectedKey = $key;
|
||||||
|
return $wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AssocWrapperContext $context
|
||||||
|
* @param AssocWrapper $wrapper
|
||||||
|
*/
|
||||||
|
static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int {
|
||||||
|
if ($params["ensure_array"] ?? $context->ensureArray) {
|
||||||
|
$valueKey = $context->valueKey;
|
||||||
|
$array = $context->input->get($valueKey);
|
||||||
|
if ($array === null) $context->input->set([], $valueKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($params["ensure_assoc"] ?? $context->ensureAssoc) {
|
||||||
|
$context->input->ensureAssoc($context->schema->getKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
$what = ScalarWrapper::_analyze($context, $wrapper, $params);
|
||||||
|
/** @var ScalarResult $result */
|
||||||
|
$result = $context->result;
|
||||||
|
if (!$result->valid) return $what;
|
||||||
|
|
||||||
|
foreach ($context->keyWrappers as $keyWrapper) {
|
||||||
|
$keyWrapper->analyze($params);
|
||||||
|
if (!$keyWrapper->isValid()) {
|
||||||
|
#XXX distinguer MISSING, UNAVAILABLE, NULL et !VALID
|
||||||
|
$what = ref_analyze::INVALID;
|
||||||
|
$result->addInvalidMessage($keyWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#XXX supprimer les clés "missing" ou "unavailable" sauf si $ensureKeys
|
||||||
|
return $what;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AssocWrapperContext $context
|
||||||
|
* @param AssocWrapper $wrapper
|
||||||
|
*/
|
||||||
|
static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool {
|
||||||
|
$ensureKeys = $params["ensure_keys"] ?? $context->ensureKeys;
|
||||||
|
$ensureOrder = $params["ensure_order"] ?? $context->ensureOrder;
|
||||||
|
if ($ensureKeys || $ensureOrder) {
|
||||||
|
$schema = $context->schema;
|
||||||
|
$keys = $schema->getKeys();
|
||||||
|
if ($ensureKeys) {
|
||||||
|
$defaults = [];
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$default = $schema->getSchema($key)->default;
|
||||||
|
if ($default === null) {
|
||||||
|
$default = $wrapper->getType($key)->getNullValue();
|
||||||
|
}
|
||||||
|
$defaults[$key] = $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($ensureKeys) $context->input->ensureKeys($defaults, $params);
|
||||||
|
if ($ensureOrder) $context->input->ensureOrder($keys, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
$modified = ScalarWrapper::_normalize($context, $wrapper, $params);
|
||||||
|
foreach ($context->keyWrappers as $keyWrapper) {
|
||||||
|
if ($keyWrapper->normalize($params)) $modified = true;
|
||||||
|
}
|
||||||
|
return $modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResult($key=false): Result {
|
||||||
|
if ($key === false) $key = $this->context->selectedKey;
|
||||||
|
return $this->_getWrapper($key)->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getType($key=false): IType {
|
||||||
|
if ($key === false) $key = $this->context->selectedKey;
|
||||||
|
return $this->_getWrapper($key)->getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($default=null, $key=false) {
|
||||||
|
$context = $this->context;
|
||||||
|
if (!$context->arrayWrapper->isAvailable()) return $default;
|
||||||
|
if ($key === false) $key = $context->selectedKey;
|
||||||
|
return $this->_getWrapper($key)->get($default);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set($value, ?array $params=null, $key=false): Wrapper {
|
||||||
|
$context = $this->context;
|
||||||
|
if ($key === false) $key = $context->selectedKey;
|
||||||
|
$this->_getWrapper($key)->set($value);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unset(?array $params=null, $key=false): Wrapper {
|
||||||
|
$context = $this->context;
|
||||||
|
if ($key === false) $key = $context->selectedKey;
|
||||||
|
$this->_getWrapper($key)->unset();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format($format=null, $key=false): string {
|
||||||
|
$context = $this->context;
|
||||||
|
if ($key === false) $key = $context->selectedKey;
|
||||||
|
return $this->_getWrapper($key)->format($format);
|
||||||
|
}
|
||||||
|
}
|
31
src/schema/_assoc/AssocWrapperContext.php
Normal file
31
src/schema/_assoc/AssocWrapperContext.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\_assoc;
|
||||||
|
|
||||||
|
use nulib\ref\schema\ref_schema;
|
||||||
|
use nulib\schema\_scalar\ScalarWrapper;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
use nulib\schema\WrapperContext;
|
||||||
|
|
||||||
|
class AssocWrapperContext extends WrapperContext {
|
||||||
|
|
||||||
|
public bool $ensureArray;
|
||||||
|
public bool $ensureAssoc;
|
||||||
|
public bool $ensureKeys;
|
||||||
|
public bool $ensureOrder;
|
||||||
|
|
||||||
|
public function resetParams(?array $params): void {
|
||||||
|
parent::resetParams($params);
|
||||||
|
$this->ensureArray = $params["ensure_array"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_array"][1];
|
||||||
|
$this->ensureAssoc = $params["ensure_assoc"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_assoc"][1];
|
||||||
|
$this->ensureKeys = $params["ensure_keys"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_keys"][1];
|
||||||
|
$this->ensureOrder = $params["ensure_order"] ?? ref_schema::ASSOC_PARAMS_SCHEMA["ensure_order"][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public ?ScalarWrapper $arrayWrapper = null;
|
||||||
|
|
||||||
|
/** liste des clés valides */
|
||||||
|
public array $keys;
|
||||||
|
|
||||||
|
/** @var Wrapper[] */
|
||||||
|
public array $keyWrappers;
|
||||||
|
}
|
@ -1,8 +1,51 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\_list;
|
namespace nulib\schema\_list;
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\ValueException;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
|
||||||
class ListResult extends Result {
|
class ListResult extends Result {
|
||||||
function isList(?ListResult &$list=null): bool { $list = $this; return true;}
|
function __construct(Result $arrayResult, array &$keyResults) {
|
||||||
|
$this->arrayResult = $arrayResult;
|
||||||
|
$this->keyResults =& $keyResults;
|
||||||
|
$this->result =& $this->arrayResult;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Result $arrayResult;
|
||||||
|
|
||||||
|
/** @var Result[] */
|
||||||
|
protected array $keyResults;
|
||||||
|
|
||||||
|
function getKeys(): array {
|
||||||
|
return array_keys($this->keyResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Result $result;
|
||||||
|
|
||||||
|
function select($key): Result {
|
||||||
|
if ($key === null) {
|
||||||
|
$this->result =& $this->arrayResult;
|
||||||
|
} elseif (array_key_exists($key, $this->keyResults)) {
|
||||||
|
$this->result =& $this->keyResults[$key];
|
||||||
|
} else {
|
||||||
|
throw ValueException::invalid_key($key);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(): void {
|
||||||
|
$this->arrayResult->reset();
|
||||||
|
foreach ($this->keyResults as $result) {
|
||||||
|
$result->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function __get(string $name) {
|
||||||
|
return $this->result[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
function __set(string $name, $value): void {
|
||||||
|
$this->result[$name] = $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\_list;
|
namespace nulib\schema\_list;
|
||||||
|
|
||||||
use nur\sery\ref\schema\ref_schema;
|
use nulib\ref\schema\ref_schema;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\Value;
|
use nulib\schema\Schema;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
|
||||||
class ListSchema extends Schema {
|
class ListSchema extends Schema {
|
||||||
/** @var array meta-schema d'un schéma de nature liste */
|
/** @var array meta-schema d'un schéma de nature liste */
|
||||||
@ -16,15 +17,8 @@ class ListSchema extends Schema {
|
|||||||
static function isa_definition($definition): bool {
|
static function isa_definition($definition): bool {
|
||||||
if (!is_array($definition)) return false;
|
if (!is_array($definition)) return false;
|
||||||
# nature explicitement spécifiée
|
# nature explicitement spécifiée
|
||||||
if (array_key_exists("", $definition)) {
|
if (self::have_nature($definition, $nature)) {
|
||||||
$nature = $definition[""];
|
return $nature === "list";
|
||||||
if ($nature === "list") return true;
|
|
||||||
if (is_array($nature)
|
|
||||||
&& array_key_exists(0, $nature)
|
|
||||||
&& $nature[0] === "list") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
# un unique élément tableau à l'index 0
|
# un unique élément tableau à l'index 0
|
||||||
$count = count($definition);
|
$count = count($definition);
|
||||||
@ -33,21 +27,50 @@ class ListSchema extends Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static function normalize($definition, $definitionKey=null): array {
|
static function normalize($definition, $definitionKey=null): array {
|
||||||
|
if (!is_array($definition)) $definition = [$definition];
|
||||||
|
if (!self::have_nature($definition)) {
|
||||||
|
$definition = [
|
||||||
|
"?array",
|
||||||
|
"" => "list",
|
||||||
|
"schema" => $definition[0],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
self::_normalize_definition($definition, $definitionKey);
|
||||||
|
self::_ensure_nature($definition, "list", "array");
|
||||||
|
return $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
|
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
|
||||||
if ($definition === null) $definition = static::SCHEMA;
|
if ($definition === null) $definition = static::SCHEMA;
|
||||||
if ($normalize) $definition = self::normalize($definition, $definitionKey);
|
if ($normalize) {
|
||||||
|
$definition = self::normalize($definition, $definitionKey);
|
||||||
|
$this->_definition = $definition;
|
||||||
|
self::_ensure_type($definition);
|
||||||
|
self::_ensure_schema_instances($definition);
|
||||||
|
}
|
||||||
$this->definition = $definition;
|
$this->definition = $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isList(?ListSchema &$list=null): bool {
|
const KEYS = [null];
|
||||||
$list = $this;
|
|
||||||
return true;
|
function getKeys(): array {
|
||||||
|
return self::KEYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newValue(?Value &$destv=null, &$dest=null, $destKey=null): ListValue {
|
public function getSchema($key=false): Schema {
|
||||||
if ($destv instanceof ListValue) return $destv->reset($dest, $destKey);
|
if ($key !== null) throw ValueException::invalid_key($key);
|
||||||
else return ($destv = new ListValue($this, $dest, $destKey));
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newWrapper(): ListWrapper {
|
||||||
|
return new ListWrapper($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWrapper(&$value=null, $valueKey=null, ?array $params = null, ?Wrapper &$wrapper=null): ListWrapper {
|
||||||
|
# si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
|
||||||
|
# cf le code similaire dans ScalarWrapper::__construct()
|
||||||
|
$verifix = $value !== null || $wrapper !== null;
|
||||||
|
if (!($wrapper instanceof ListWrapper)) $wrapper = $this->newWrapper();
|
||||||
|
return $wrapper->reset($value, $valueKey, $verifix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\wip\schema\_list;
|
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
|
||||||
use nur\sery\wip\schema\Value;
|
|
||||||
|
|
||||||
class ListValue extends Value {
|
|
||||||
function isList(?ListValue &$list=null): bool { $list = $this; return true; }
|
|
||||||
|
|
||||||
function ensureKeys(): bool {
|
|
||||||
}
|
|
||||||
|
|
||||||
function orderKeys(): bool {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param Result[] $results */
|
|
||||||
function verifix(bool $throw=true, ?array &$results=null): bool {
|
|
||||||
}
|
|
||||||
}
|
|
17
src/schema/_list/ListWrapper.php
Normal file
17
src/schema/_list/ListWrapper.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\_list;
|
||||||
|
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
|
||||||
|
abstract/*XXX*/ class ListWrapper extends Wrapper {
|
||||||
|
function ensureKeys(): bool {
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderKeys(): bool {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param Result[] $results */
|
||||||
|
function verifix(bool $throw=true, ?array &$results=null): bool {
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +1,29 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\_scalar;
|
namespace nulib\schema\_scalar;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
use nur\sery\ref\schema\ref_analyze;
|
use nulib\ref\schema\ref_analyze;
|
||||||
use nur\sery\ref\schema\ref_schema;
|
use nulib\ref\schema\ref_schema;
|
||||||
use nur\sery\ValueException;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ScalarResult: résultat de l'analyse ou de la normalisation d'une valeur
|
* Class ScalarResult: résultat de l'analyse ou de la normalisation d'une valeur
|
||||||
*
|
|
||||||
* @property bool $resultAvailable le résultat est-il disponible?
|
|
||||||
* @property bool $present la valeur existe-t-elle?
|
|
||||||
* @property bool $available si la valeur existe, est-elle disponible?
|
|
||||||
* @property bool $null si la valeur est disponible, est-elle nulle?
|
|
||||||
* @property bool $valid si la valeur est disponible, est-elle valide?
|
|
||||||
* @property bool $normalized si la valeur est valide, est-elle normalisée?
|
|
||||||
* @property string|null $orig valeur originale avant analyse avec parse()
|
|
||||||
* @property string|null $message message si la valeur n'est pas valide
|
|
||||||
*/
|
*/
|
||||||
class ScalarResult extends Result {
|
class ScalarResult extends Result {
|
||||||
const KEYS = ["resultAvailable", "present", "available", "null", "valid", "normalized", "orig", "message"];
|
|
||||||
|
|
||||||
function isScalar(?ScalarResult &$scalar=null): bool { $scalar = $this; return true; }
|
|
||||||
|
|
||||||
function getKeys(): array {
|
function getKeys(): array {
|
||||||
return [null];
|
return ScalarSchema::KEYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getResult($key=null): Result {
|
function select($key): Result {
|
||||||
if ($key === null) return $this;
|
if ($key !== null) throw ValueException::invalid_key($key);
|
||||||
else throw ValueException::invalid_key($key);
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var array */
|
protected array $result;
|
||||||
protected $result;
|
|
||||||
|
|
||||||
function reset(): void {
|
function reset(): void {
|
||||||
$this->result = array_merge(
|
$this->result = array_merge(
|
||||||
@ -56,26 +45,13 @@ class ScalarResult extends Result {
|
|||||||
$this->result[$name] = $value;
|
$this->result[$name] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function replace_key(string &$message, ?string $key): void {
|
protected function getMessage(string $key, Schema $schema): string {
|
||||||
if ($key) {
|
|
||||||
$message = str_replace("{key}", $key, $message);
|
|
||||||
} else {
|
|
||||||
$message = str_replace("{key}: ", "", $message);
|
|
||||||
$message = str_replace("cette valeur", "la valeur", $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function replace_orig(string &$message, $orig): void {
|
|
||||||
$message = str_replace("{orig}", strval($orig), $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMessage(string $key, ScalarSchema $schema): string {
|
|
||||||
$message = cl::get($schema->messages, $key);
|
$message = cl::get($schema->messages, $key);
|
||||||
if ($message !== null) return $message;
|
if ($message !== null) return $message;
|
||||||
return cl::get(ref_schema::MESSAGES, $key);
|
return cl::get(ref_schema::MESSAGES, $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMissing(ScalarSchema $schema): int {
|
function setMissing( Schema $schema): int {
|
||||||
$this->resultAvailable = true;
|
$this->resultAvailable = true;
|
||||||
$this->present = false;
|
$this->present = false;
|
||||||
$this->available = false;
|
$this->available = false;
|
||||||
@ -85,14 +61,13 @@ class ScalarResult extends Result {
|
|||||||
$this->normalized = true;
|
$this->normalized = true;
|
||||||
return ref_analyze::NORMALIZED;
|
return ref_analyze::NORMALIZED;
|
||||||
} else {
|
} else {
|
||||||
$message = $this->getMessage("missing", $schema);
|
$this->messageKey = $messageKey = "missing";
|
||||||
self::replace_key($message, $schema->name);
|
$this->message = $this->getMessage($messageKey, $schema);
|
||||||
$this->message = $message;
|
|
||||||
return ref_analyze::MISSING;
|
return ref_analyze::MISSING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUnavailable(ScalarSchema $schema): int {
|
function setUnavailable( Schema $schema): int {
|
||||||
$this->resultAvailable = true;
|
$this->resultAvailable = true;
|
||||||
$this->present = true;
|
$this->present = true;
|
||||||
$this->available = false;
|
$this->available = false;
|
||||||
@ -102,14 +77,13 @@ class ScalarResult extends Result {
|
|||||||
$this->normalized = true;
|
$this->normalized = true;
|
||||||
return ref_analyze::NORMALIZED;
|
return ref_analyze::NORMALIZED;
|
||||||
} else {
|
} else {
|
||||||
$message = $this->getMessage("unavailable", $schema);
|
$this->messageKey = $messageKey = "unavailable";
|
||||||
self::replace_key($message, $schema->name);
|
$this->message = $this->getMessage($messageKey, $schema);
|
||||||
$this->message = $message;
|
|
||||||
return ref_analyze::UNAVAILABLE;
|
return ref_analyze::UNAVAILABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNull(ScalarSchema $schema): int {
|
function setNull( Schema $schema): int {
|
||||||
$this->resultAvailable = true;
|
$this->resultAvailable = true;
|
||||||
$this->present = true;
|
$this->present = true;
|
||||||
$this->available = true;
|
$this->available = true;
|
||||||
@ -119,33 +93,60 @@ class ScalarResult extends Result {
|
|||||||
$this->normalized = true;
|
$this->normalized = true;
|
||||||
return ref_analyze::NORMALIZED;
|
return ref_analyze::NORMALIZED;
|
||||||
} else {
|
} else {
|
||||||
$message = $this->getMessage("null", $schema);
|
$this->messageKey = $messageKey = "null";
|
||||||
self::replace_key($message, $schema->name);
|
$this->message = $this->getMessage($messageKey, $schema);
|
||||||
$this->message = $message;
|
|
||||||
return ref_analyze::NULL;
|
return ref_analyze::NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInvalid($value, ScalarSchema $schema): int {
|
function setInvalid($value, Schema $schema, ?Throwable $exception=null): int {
|
||||||
$this->resultAvailable = true;
|
$this->resultAvailable = true;
|
||||||
$this->present = true;
|
$this->present = true;
|
||||||
$this->available = true;
|
$this->available = true;
|
||||||
$this->null = false;
|
$this->null = false;
|
||||||
$this->valid = false;
|
$this->valid = false;
|
||||||
$this->orig = $value;
|
$this->origValue = $value;
|
||||||
$message = $this->getMessage("invalid", $schema);
|
$this->messageKey = $messageKey = "invalid";
|
||||||
self::replace_key($message, $schema->name);
|
$message = $this->getMessage($messageKey, $schema);
|
||||||
self::replace_orig($message, $schema->orig);
|
if ($exception !== null) {
|
||||||
|
$tmessage = ValueException::get_message($exception);
|
||||||
|
if ($tmessage) $message = $tmessage;
|
||||||
|
}
|
||||||
$this->message = $message;
|
$this->message = $message;
|
||||||
|
$this->exception = $exception;
|
||||||
return ref_analyze::INVALID;
|
return ref_analyze::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setValid(): int {
|
function addInvalidMessage(Wrapper $wrapper): void {
|
||||||
|
$this->resultAvailable = true;
|
||||||
|
$this->present = true;
|
||||||
|
$this->available = true;
|
||||||
|
$this->null = false;
|
||||||
|
$this->valid = false;
|
||||||
|
$this->messageKey = "invalid";
|
||||||
|
$result = $wrapper->getResult();
|
||||||
|
$resultException = $result->exception;
|
||||||
|
$resultMessage = $result->message;
|
||||||
|
if ($resultException !== null) {
|
||||||
|
$tmessage = ValueException::get_message($resultException);
|
||||||
|
if ($tmessage) {
|
||||||
|
if ($resultMessage !== null) $resultMessage .= ": ";
|
||||||
|
$resultMessage .= $tmessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$message = $this->message;
|
||||||
|
if ($message) $message .= "\n";
|
||||||
|
$message .= $resultMessage;
|
||||||
|
$this->message = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setValid($normalizedValue=null): int {
|
||||||
$this->resultAvailable = true;
|
$this->resultAvailable = true;
|
||||||
$this->present = true;
|
$this->present = true;
|
||||||
$this->available = true;
|
$this->available = true;
|
||||||
$this->null = false;
|
$this->null = false;
|
||||||
$this->valid = true;
|
$this->valid = true;
|
||||||
|
$this->normalizedValue = $normalizedValue;
|
||||||
return ref_analyze::VALID;
|
return ref_analyze::VALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,6 +161,10 @@ class ScalarResult extends Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function throw(bool $throw): void {
|
function throw(bool $throw): void {
|
||||||
if ($throw) throw new ValueException($this->message);
|
if ($throw) {
|
||||||
|
$exception = $this->exception;
|
||||||
|
if ($exception !== null) throw $exception;
|
||||||
|
else throw new ValueException($this->message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\_scalar;
|
namespace nulib\schema\_scalar;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
use nur\sery\ref\schema\ref_schema;
|
use nulib\ref\schema\ref_schema;
|
||||||
use nur\sery\ref\schema\ref_types;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\Schema;
|
||||||
use nur\sery\wip\schema\SchemaException;
|
use nulib\schema\Wrapper;
|
||||||
use nur\sery\wip\schema\types\tarray;
|
|
||||||
use nur\sery\wip\schema\types\tbool;
|
|
||||||
use nur\sery\wip\schema\types\tcallable;
|
|
||||||
use nur\sery\wip\schema\types\tcontent;
|
|
||||||
use nur\sery\wip\schema\types\tpkey;
|
|
||||||
use nur\sery\wip\schema\types\tstring;
|
|
||||||
use nur\sery\wip\schema\Value;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ScalarSchema
|
* Class ScalarSchema
|
||||||
*
|
|
||||||
* @property-read array $type
|
|
||||||
* @property-read mixed $default
|
|
||||||
* @property-read string|null $title
|
|
||||||
* @property-read bool $required
|
|
||||||
* @property-read bool $nullable
|
|
||||||
* @property-read string|array|null $desc
|
|
||||||
* @property-read callable|null $analyzerFunc
|
|
||||||
* @property-read callable|null $extractorFunc
|
|
||||||
* @property-read callable|null $parserFunc
|
|
||||||
* @property-read callable|null $normalizerFunc
|
|
||||||
* @property-read array|null $messages
|
|
||||||
* @property-read callable|null $formatterFunc
|
|
||||||
* @property-read mixed $format
|
|
||||||
* @property-read array $nature
|
|
||||||
* @property-read string|int|null $name
|
|
||||||
* @property-read string|array|null $pkey
|
|
||||||
* @property-read string|null $header
|
|
||||||
* @property-read bool|null $composite
|
|
||||||
*/
|
*/
|
||||||
class ScalarSchema extends Schema {
|
class ScalarSchema extends Schema {
|
||||||
/** @var array meta-schema d'un schéma de nature scalaire */
|
|
||||||
const METASCHEMA = ref_schema::SCALAR_METASCHEMA;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* indiquer si $definition est une définition de schéma scalaire que
|
* indiquer si $definition est une définition de schéma scalaire que
|
||||||
* {@link normalize()} pourrait normaliser
|
* {@link normalize_definition()} pourrait normaliser
|
||||||
*/
|
*/
|
||||||
static function isa_definition($definition): bool {
|
static function isa_definition($definition): bool {
|
||||||
# chaine ou null
|
# chaine ou null
|
||||||
@ -71,123 +42,57 @@ class ScalarSchema extends Schema {
|
|||||||
return $haveIndex0 && $count > 1;
|
return $haveIndex0 && $count > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function normalize($definition, $definitionKey=null): array {
|
static function normalize_definition($definition, $definitionKey=null): array {
|
||||||
if (!is_array($definition)) $definition = [$definition];
|
$natureMetaschema = array_merge(ref_schema::NATURE_METASCHEMA, ref_schema::SCALAR_NATURE_METASCHEMA);
|
||||||
# s'assurer que toutes les clés existent avec leur valeur par défaut
|
self::_normalize_definition($definition, $definitionKey, $natureMetaschema);
|
||||||
$index = 0;
|
self::_ensure_nature($definition, "scalar");
|
||||||
foreach (array_keys(self::METASCHEMA) as $key) {
|
|
||||||
if (!array_key_exists($key, $definition)) {
|
|
||||||
if (array_key_exists($index, $definition)) {
|
|
||||||
$definition[$key] = $definition[$index];
|
|
||||||
unset($definition[$index]);
|
|
||||||
$index++;
|
|
||||||
} else {
|
|
||||||
$definition[$key] = self::METASCHEMA[$key][1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# réordonner les clés numériques
|
|
||||||
if (cl::have_num_keys($definition)) {
|
|
||||||
$keys = array_keys($definition);
|
|
||||||
$index = 0;
|
|
||||||
foreach ($keys as $key) {
|
|
||||||
if (!is_int($key)) continue;
|
|
||||||
$definition[$index] = $definition[$key];
|
|
||||||
unset($definition[$key]);
|
|
||||||
$index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# type
|
|
||||||
$types = [];
|
|
||||||
$deftype = $definition["type"];
|
|
||||||
$nullable = $definition["nullable"];
|
|
||||||
if ($deftype === null) {
|
|
||||||
$types[] = null;
|
|
||||||
$nullable = true;
|
|
||||||
} else {
|
|
||||||
if (!is_array($deftype)) {
|
|
||||||
if (!is_string($deftype)) throw SchemaException::invalid_type($deftype);
|
|
||||||
$deftype = explode("|", $deftype);
|
|
||||||
}
|
|
||||||
foreach ($deftype as $type) {
|
|
||||||
if ($type === null || $type === "null") {
|
|
||||||
$nullable = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!is_string($type)) throw SchemaException::invalid_type($type);
|
|
||||||
if (substr($type, 0, 1) == "?") {
|
|
||||||
$type = substr($type, 1);
|
|
||||||
$nullable = true;
|
|
||||||
}
|
|
||||||
if ($type === "") throw SchemaException::invalid_type($type);
|
|
||||||
$type = cl::get(ref_types::ALIASES, $type, $type);
|
|
||||||
$types = array_merge($types, explode("|", $type));
|
|
||||||
}
|
|
||||||
if (!$types) throw SchemaException::invalid_schema("scalar: type is required");
|
|
||||||
$types = array_keys(array_fill_keys($types, true));
|
|
||||||
}
|
|
||||||
$definition["type"] = $types;
|
|
||||||
$definition["nullable"] = $nullable;
|
|
||||||
# nature
|
|
||||||
$nature = $definition[""];
|
|
||||||
tarray::ensure_array($nature);
|
|
||||||
if (!array_key_exists(0, $nature) || $nature[0] !== "scalar") {
|
|
||||||
throw SchemaException::invalid_schema("expected scalar nature");
|
|
||||||
}
|
|
||||||
$definition[""] = $nature;
|
|
||||||
# name, pkey, header
|
|
||||||
$name = $definition["name"];
|
|
||||||
$pkey = $definition["pkey"];
|
|
||||||
$header = $definition["header"];
|
|
||||||
if ($name === null) $name = $definitionKey;
|
|
||||||
tstring::ensure_nstring($name);
|
|
||||||
tpkey::ensure_npkey($pkey);
|
|
||||||
tstring::ensure_nstring($header);
|
|
||||||
if ($pkey === null) $pkey = $name;
|
|
||||||
if ($header === null) $header = $name;
|
|
||||||
$definition["name"] = $name;
|
|
||||||
$definition["pkey"] = $pkey;
|
|
||||||
$definition["header"] = $header;
|
|
||||||
# autres éléments
|
|
||||||
tstring::ensure_nstring($definition["title"]);
|
|
||||||
tbool::ensure_bool($definition["required"]);
|
|
||||||
tbool::ensure_bool($definition["nullable"]);
|
|
||||||
tcontent::ensure_ncontent($definition["desc"]);
|
|
||||||
tcallable::ensure_ncallable($definition["analyzer_func"]);
|
|
||||||
tcallable::ensure_ncallable($definition["extractor_func"]);
|
|
||||||
tcallable::ensure_ncallable($definition["parser_func"]);
|
|
||||||
tcallable::ensure_ncallable($definition["normalizer_func"]);
|
|
||||||
tarray::ensure_narray($definition["messages"]);
|
|
||||||
tcallable::ensure_ncallable($definition["formatter_func"]);
|
|
||||||
tbool::ensure_nbool($definition["composite"]);
|
|
||||||
return $definition;
|
return $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
|
function __construct($definition=null, $definitionKey=null, bool $normalize=true) {
|
||||||
if ($definition === null) $definition = static::SCHEMA;
|
if ($definition === null) $definition = static::SCHEMA;
|
||||||
if ($normalize) $definition = self::normalize($definition, $definitionKey);
|
if ($normalize) {
|
||||||
|
$definition = self::normalize_definition($definition, $definitionKey);
|
||||||
|
$this->_definition = $definition;
|
||||||
|
self::_ensure_type($definition);
|
||||||
|
self::_ensure_schema_instances($definition);
|
||||||
|
} else {
|
||||||
|
# ici, $definition contient un schema déjà instancié, mais c'est le mieux
|
||||||
|
# qu'on puisse faire
|
||||||
|
$this->_definition = $definition;
|
||||||
|
}
|
||||||
$this->definition = $definition;
|
$this->definition = $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isScalar(?ScalarSchema &$scalar=null): bool {
|
const KEYS = [null];
|
||||||
$scalar = $this;
|
|
||||||
return true;
|
function getKeys(): array {
|
||||||
|
return self::KEYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newValue(?Value &$destv=null, &$dest=null, $destKey=null): ScalarValue {
|
function getSchema($key=false): Schema {
|
||||||
if ($destv instanceof ScalarValue) return $destv->reset($dest, $destKey);
|
if ($key === null || $key === false) return $this;
|
||||||
else return ($destv = new ScalarValue($this, $dest, $destKey));
|
throw ValueException::invalid_key($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
#############################################################################
|
protected function newWrapper(): ScalarWrapper {
|
||||||
# key & properties
|
return new ScalarWrapper($this);
|
||||||
|
}
|
||||||
|
|
||||||
const _PROPERTY_PKEYS = [
|
function getWrapper(&$value=null, $valueKey=null, ?array $params=null, ?Wrapper &$wrapper=null): ScalarWrapper {
|
||||||
"analyzerFunc" => "analyzer_func",
|
# si pas de valeur ni de wrapper, pas de vérification et donc pas d'exception
|
||||||
"extractorFunc" => "extractor_func",
|
# cf le code similaire dans ScalarWrapper::__construct()
|
||||||
"parserFunc" => "parser_func",
|
$dontAnalyze = $value === null && $wrapper === null;
|
||||||
"normalizerFunc" => "normalizer_func",
|
if (!($wrapper instanceof ScalarWrapper)) $wrapper = $this->newWrapper();
|
||||||
"formatterFunc" => "formatter_func",
|
|
||||||
"nature" => ["", 0],
|
# la nature du schéma peut contenir des paramètres par défaut
|
||||||
];
|
$nature = $this->definition[""];
|
||||||
|
foreach (array_keys(ref_schema::SCALAR_PARAMS_SCHEMA) as $paramKey) {
|
||||||
|
$paramValue = $nature[$paramKey] ?? null;
|
||||||
|
if ($paramValue !== null) $params[$paramKey] = $paramValue;
|
||||||
|
}
|
||||||
|
if ($params !== null) $wrapper->resetParams($params);
|
||||||
|
|
||||||
|
return $wrapper->reset($value, $valueKey, $dontAnalyze? ["analyze" => false]: null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,198 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\wip\schema\_scalar;
|
|
||||||
|
|
||||||
use nur\sery\ref\schema\ref_analyze;
|
|
||||||
use nur\sery\ValueException;
|
|
||||||
use nur\sery\wip\schema\input\Input;
|
|
||||||
use nur\sery\wip\schema\types;
|
|
||||||
use nur\sery\wip\schema\types\IType;
|
|
||||||
use nur\sery\wip\schema\Value;
|
|
||||||
|
|
||||||
class ScalarValue extends Value {
|
|
||||||
function __construct(ScalarSchema $schema, &$dest=null, $destKey=null, bool $defaultVerifix=true, ?bool $defaultThrow=null) {
|
|
||||||
if ($dest !== null && $defaultThrow = null) {
|
|
||||||
# Si $dest est null, ne pas lancer d'exception, parce qu'on considère que
|
|
||||||
# c'est une initialisation sans conséquences
|
|
||||||
$defaultThrow = true;
|
|
||||||
}
|
|
||||||
$this->schema = $schema;
|
|
||||||
$this->defaultVerifix = $defaultVerifix;
|
|
||||||
$this->defaultThrow = $defaultThrow !== null? $defaultThrow: false;
|
|
||||||
$this->result = new ScalarResult();
|
|
||||||
$this->reset($dest, $destKey);
|
|
||||||
$this->defaultThrow = $defaultThrow !== null? $defaultThrow: true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isScalar(?ScalarValue &$scalar=null): bool { $scalar = $this; return true; }
|
|
||||||
|
|
||||||
/** @var ScalarSchema schéma de cette valeur */
|
|
||||||
protected $schema;
|
|
||||||
|
|
||||||
/** @var Input source et destination de la valeur */
|
|
||||||
protected $input;
|
|
||||||
|
|
||||||
/** @var string|int|null clé de la valeur dans le tableau destination */
|
|
||||||
protected $destKey;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
protected $defaultVerifix;
|
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
protected $defaultThrow;
|
|
||||||
|
|
||||||
/** @var IType type de la valeur après analyse */
|
|
||||||
protected $type;
|
|
||||||
|
|
||||||
/** @var ?ScalarResult résultat de l'analyse de la valeur */
|
|
||||||
protected $result;
|
|
||||||
|
|
||||||
function reset(&$dest, $destKey=null, ?bool $verifix=null): Value {
|
|
||||||
if ($dest instanceof Input) $input = $dest;
|
|
||||||
else $input = new Input($dest);
|
|
||||||
$this->input = $input;
|
|
||||||
$this->destKey = $destKey;
|
|
||||||
$this->type = null;
|
|
||||||
$this->_analyze();
|
|
||||||
if ($verifix == null) $verifix = $this->defaultVerifix;
|
|
||||||
if ($verifix) $this->verifix();
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getKeys(): array {
|
|
||||||
return [null];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getValue($key=null): ScalarValue {
|
|
||||||
if ($key === null) return $this;
|
|
||||||
throw ValueException::invalid_key($key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** analyser la valeur et résoudre son type */
|
|
||||||
function _analyze(): int {
|
|
||||||
$schema = $this->schema;
|
|
||||||
$input = $this->input;
|
|
||||||
$destKey = $this->destKey;
|
|
||||||
$result = $this->result;
|
|
||||||
$result->reset();
|
|
||||||
if (!$input->isPresent($destKey)) return $result->setMissing($schema);
|
|
||||||
$haveType = false;
|
|
||||||
$types = [];
|
|
||||||
$type = $firstType = null;
|
|
||||||
$haveValue = false;
|
|
||||||
$value = null;
|
|
||||||
# d'abord chercher un type pour lequel c'est une valeur normalisée
|
|
||||||
foreach ($schema->type as $name) {
|
|
||||||
$type = types::get($name);
|
|
||||||
if ($firstType === null) $firstType = $type;
|
|
||||||
$types[] = $type;
|
|
||||||
if ($type->isAvailable($input, $destKey)) {
|
|
||||||
if (!$haveValue) {
|
|
||||||
$value = $input->get($destKey);
|
|
||||||
$haveValue = true;
|
|
||||||
}
|
|
||||||
if ($type->isValid($value, $normalized) && $normalized) {
|
|
||||||
$haveType = true;
|
|
||||||
$this->type = $type;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$haveType) {
|
|
||||||
# ensuite chercher un type pour lequel la valeur est valide
|
|
||||||
foreach ($types as $type) {
|
|
||||||
if ($type->isAvailable($input, $destKey) && $type->isValid($value)) {
|
|
||||||
$haveType = true;
|
|
||||||
$this->type = $type;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# sinon prendre le premier type
|
|
||||||
if (!$haveType) $type = $this->type = $firstType;
|
|
||||||
if (!$type->isAvailable($input, $destKey)) return $result->setUnavailable($schema);
|
|
||||||
$value = $input->get($destKey);
|
|
||||||
if ($type->isNull($value)) return $result->setNull($schema);
|
|
||||||
if ($type->isValid($value, $normalized)) {
|
|
||||||
if ($normalized) return $result->setNormalized();
|
|
||||||
else return $result->setValid();
|
|
||||||
}
|
|
||||||
if (is_string($value)) return ref_analyze::STRING;
|
|
||||||
else return $result->setInvalid($value, $schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifix(?bool $throw=null): bool {
|
|
||||||
if ($throw === null) $throw = $this->defaultThrow;
|
|
||||||
$destKey = $this->destKey;
|
|
||||||
$verifix = false;
|
|
||||||
$result =& $this->result;
|
|
||||||
$modified = false;
|
|
||||||
if ($result->resultAvailable) {
|
|
||||||
if ($result->null) {
|
|
||||||
# forcer la valeur null, parce que la valeur actuelle est peut-être une
|
|
||||||
# valeur assimilée à null
|
|
||||||
$this->input->set(null, $destKey);
|
|
||||||
} elseif ($result->valid && !$result->normalized) {
|
|
||||||
# normaliser la valeur
|
|
||||||
$verifix = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$verifix = true;
|
|
||||||
}
|
|
||||||
if ($verifix) {
|
|
||||||
$value = $this->input->get($destKey);
|
|
||||||
$modified = $this->type->verifix($value, $result, $this->schema);
|
|
||||||
if ($result->valid) $this->input->set($value, $destKey);
|
|
||||||
}
|
|
||||||
if (!$result->valid) $result->throw($throw);
|
|
||||||
return $modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getResult(): ScalarResult {
|
|
||||||
return $this->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPresent(): bool {
|
|
||||||
return $this->result->present;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getType(): IType {
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAvailable(): bool {
|
|
||||||
return $this->result->available;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValid(): bool {
|
|
||||||
return $this->result->valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNormalized(): bool {
|
|
||||||
return $this->result->normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get($default=null) {
|
|
||||||
if ($this->result->available) return $this->input->get($this->destKey);
|
|
||||||
else return $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
function set($value, ?bool $verifix=null): ScalarValue {
|
|
||||||
$this->input->set($value, $this->destKey);
|
|
||||||
$this->_analyze();
|
|
||||||
if ($verifix === null) $verifix = $this->defaultVerifix;
|
|
||||||
if ($verifix) $this->verifix();
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
function unset(?bool $verifix=null): ScalarValue {
|
|
||||||
$this->input->unset($this->destKey);
|
|
||||||
$this->_analyze();
|
|
||||||
if ($verifix === null) $verifix = $this->defaultVerifix;
|
|
||||||
if ($verifix) $this->verifix();
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
function format($format=null): string {
|
|
||||||
return $this->type->format($this->input->get($this->destKey), $format);
|
|
||||||
}
|
|
||||||
}
|
|
238
src/schema/_scalar/ScalarWrapper.php
Normal file
238
src/schema/_scalar/ScalarWrapper.php
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\_scalar;
|
||||||
|
|
||||||
|
use nulib\php\func;
|
||||||
|
use nulib\ref\schema\ref_analyze;
|
||||||
|
use nulib\ValueException;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
use nulib\schema\types;
|
||||||
|
use nulib\schema\types\IType;
|
||||||
|
use nulib\schema\Wrapper;
|
||||||
|
use nulib\schema\WrapperContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ScalarWrapper
|
||||||
|
*
|
||||||
|
* @method ScalarWrapper reset(&$value, $valueKey=null, ?array $params=null)
|
||||||
|
* @method ScalarResult getResult($key=false)
|
||||||
|
* @method self set($value, ?array $params=null, $key=false)
|
||||||
|
* @method self unset(?array $params=null, $key=false)
|
||||||
|
*/
|
||||||
|
class ScalarWrapper extends Wrapper {
|
||||||
|
function __construct(Schema $schema, &$value=null, $valueKey=null, ?array $params=null, ?WrapperContext $context=null) {
|
||||||
|
if ($context === null) $context = new WrapperContext($schema, null, null, $params);
|
||||||
|
$context->result = new ScalarResult();
|
||||||
|
$this->context = $context;
|
||||||
|
|
||||||
|
if ($value !== null) {
|
||||||
|
# n'initialiser que si $value n'est pas null
|
||||||
|
$this->reset($value, $valueKey);
|
||||||
|
} else {
|
||||||
|
# il faut au moins que le type soit disponible
|
||||||
|
$this->resetContext(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WrapperContext $context;
|
||||||
|
|
||||||
|
function getKeys(): array {
|
||||||
|
return ScalarSchema::KEYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param string|int|null $key */
|
||||||
|
function select($key): ScalarWrapper {
|
||||||
|
if ($key !== null) throw ValueException::invalid_key($key);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** analyser la valeur et résoudre son type */
|
||||||
|
protected static function _analyze0(WrapperContext $context): int {
|
||||||
|
/** @var ScalarSchema $schema */
|
||||||
|
$schema = $context->schema;
|
||||||
|
$input = $context->input;
|
||||||
|
$valueKey = $context->valueKey;
|
||||||
|
/** @var ScalarResult $result */
|
||||||
|
$result = $context->result;
|
||||||
|
|
||||||
|
$default = $schema->default;
|
||||||
|
if (!$input->isPresent($valueKey)) {
|
||||||
|
if ($default !== null) {
|
||||||
|
$input->set($default, $valueKey);
|
||||||
|
return $result->setNormalized();
|
||||||
|
} else {
|
||||||
|
return $result->setMissing($schema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$schemaTypes = $schema->type;
|
||||||
|
if ($schemaTypes instanceof IType) {
|
||||||
|
$type = $schemaTypes;
|
||||||
|
} else {
|
||||||
|
# type union
|
||||||
|
$haveType = false;
|
||||||
|
$types = [];
|
||||||
|
$type = $firstType = null;
|
||||||
|
$value = null;
|
||||||
|
# d'abord chercher un type pour lequel c'est une valeur normalisée
|
||||||
|
$index = 0;
|
||||||
|
$haveValue = false;
|
||||||
|
foreach ($schemaTypes as $key => $name) {
|
||||||
|
if ($key === $index) {
|
||||||
|
$index++;
|
||||||
|
$args = null;
|
||||||
|
} else {
|
||||||
|
$args = $name;
|
||||||
|
$name = $key;
|
||||||
|
}
|
||||||
|
$type = types::get($schema->nullable, $name, $args, $schema->getDefinition());
|
||||||
|
if ($firstType === null) $firstType = $type;
|
||||||
|
$types[] = $type;
|
||||||
|
if ($type->isAvailable($input, $valueKey)) {
|
||||||
|
if (!$haveValue) {
|
||||||
|
$value = $input->get($valueKey);
|
||||||
|
$haveValue = true;
|
||||||
|
}
|
||||||
|
if ($type->isValid($value, $normalized) && $normalized) {
|
||||||
|
$haveType = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# ensuite chercher un type pour lequel la valeur est valide
|
||||||
|
if (!$haveType) {
|
||||||
|
foreach ($types as $type) {
|
||||||
|
if ($type->isAvailable($input, $valueKey) && $type->isValid($value)) {
|
||||||
|
$haveType = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# sinon prendre le premier type
|
||||||
|
if (!$haveType) {
|
||||||
|
$type = $firstType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$context->type = $type;
|
||||||
|
|
||||||
|
if (!$type->isAvailable($input, $valueKey)) {
|
||||||
|
if ($default !== null) {
|
||||||
|
$input->set($default, $valueKey);
|
||||||
|
return $result->setNormalized();
|
||||||
|
} else {
|
||||||
|
return $result->setUnavailable($schema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $input->get($valueKey);
|
||||||
|
$context->origValue = $context->value = $value;
|
||||||
|
if ($type->isNull($value)) {
|
||||||
|
return $result->setNull($schema);
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
return ref_analyze::STRING;
|
||||||
|
} elseif ($type->isValid($value, $normalized)) {
|
||||||
|
if ($normalized) return $result->setNormalized();
|
||||||
|
else return $result->setValid();
|
||||||
|
} else {
|
||||||
|
return $result->setInvalid($value, $schema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ScalarWrapper $wrapper
|
||||||
|
*/
|
||||||
|
static function _analyze(WrapperContext $context, Wrapper $wrapper, ?array $params): int {
|
||||||
|
/** @var ScalarSchema $schema */
|
||||||
|
$schema = $context->schema;
|
||||||
|
$input = $context->input;
|
||||||
|
$valueKey = $context->valueKey;
|
||||||
|
/** @var ScalarResult $result */
|
||||||
|
$result = $context->result;
|
||||||
|
|
||||||
|
/** @var func $analyzerFunc */
|
||||||
|
$analyzerFunc = $schema->analyzerFunc;
|
||||||
|
if ($analyzerFunc !== null) $what = $analyzerFunc->invoke([$context, $wrapper]);
|
||||||
|
else $what = self::_analyze0($context);
|
||||||
|
if ($what !== ref_analyze::STRING) return $what;
|
||||||
|
|
||||||
|
$value = $context->value;
|
||||||
|
try {
|
||||||
|
/** @var func $extractorFunc */
|
||||||
|
$extractorFunc = $schema->extractorFunc;
|
||||||
|
if ($extractorFunc !== null) $extracted = $extractorFunc->invoke([$value, $context, $wrapper]);
|
||||||
|
else $extracted = $context->type->extract($value);
|
||||||
|
$context->value = $extracted;
|
||||||
|
} catch (ValueException $e) {
|
||||||
|
return $result->setInvalid($context->origValue, $schema, $e);
|
||||||
|
}
|
||||||
|
if ($context->type->isNull($extracted)) return $result->setNull($schema);
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var func $parserFunc */
|
||||||
|
$parserFunc = $schema->parserFunc;
|
||||||
|
if ($parserFunc !== null) $parsed = $parserFunc->invoke([$extracted, $context, $wrapper]);
|
||||||
|
else $parsed = $context->type->parse($extracted);
|
||||||
|
$context->value = $parsed;
|
||||||
|
} catch (ValueException $e) {
|
||||||
|
return $result->setInvalid($context->origValue, $schema, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalized = $parsed === $context->origValue;
|
||||||
|
if ($normalized) {
|
||||||
|
$input->set($parsed, $valueKey);
|
||||||
|
return $result->setNormalized();
|
||||||
|
} else {
|
||||||
|
$input->set($extracted, $valueKey);
|
||||||
|
return $result->setValid($parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ScalarWrapper $wrapper
|
||||||
|
*/
|
||||||
|
static function _normalize(WrapperContext $context, Wrapper $wrapper, ?array $params): bool {
|
||||||
|
/** @var ScalarSchema $schema */
|
||||||
|
$schema = $context->schema;
|
||||||
|
$input = $context->input;
|
||||||
|
$valueKey = $context->valueKey;
|
||||||
|
/** @var ScalarResult $result */
|
||||||
|
$result = $context->result;
|
||||||
|
|
||||||
|
$normalize = false;
|
||||||
|
$modified = false;
|
||||||
|
if ($result->resultAvailable) {
|
||||||
|
if ($result->null) {
|
||||||
|
# forcer la valeur null, parce que la valeur actuelle est peut-être une
|
||||||
|
# valeur assimilée à null
|
||||||
|
$input->set(null, $valueKey);
|
||||||
|
} elseif ($result->valid && !$result->normalized) {
|
||||||
|
$normalizedValue = $result->normalizedValue;
|
||||||
|
if ($normalizedValue !== null) {
|
||||||
|
# la valeur normalisée est disponible
|
||||||
|
$input->set($normalizedValue, $valueKey);
|
||||||
|
$result->normalizedValue = null;
|
||||||
|
$modified = true;
|
||||||
|
} else {
|
||||||
|
# normaliser la valeur
|
||||||
|
$normalize = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$normalize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($normalize) {
|
||||||
|
$value = $input->get($valueKey);
|
||||||
|
/** @var func $normalizerFunc */
|
||||||
|
$normalizerFunc = $schema->normalizerFunc;
|
||||||
|
if ($normalizerFunc !== null) {
|
||||||
|
$orig = $value;
|
||||||
|
$value = $normalizerFunc->invoke([$orig, $context, $wrapper]);
|
||||||
|
$modified = $value !== $orig;
|
||||||
|
} else {
|
||||||
|
$modified = $context->type->normalize($value, $result, $schema);
|
||||||
|
}
|
||||||
|
if ($result->valid) $input->set($value, $valueKey);
|
||||||
|
}
|
||||||
|
return $modified;
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\input;
|
namespace nulib\schema\input;
|
||||||
|
|
||||||
#XXX implémenter le renommage de paramètres et faire des méthodes pour
|
#XXX implémenter le renommage de paramètres et faire des méthodes pour
|
||||||
# construire des querystring et paramètres de formulaires
|
# construire des querystring et paramètres de formulaires
|
||||||
use nur\sery\wip\php\access\FormAccess;
|
use nulib\php\access\FormAccess;
|
||||||
use nur\sery\wip\php\access\IAccess;
|
use nulib\php\access\IAccess;
|
||||||
use nur\sery\wip\php\access\KeyAccess;
|
use nulib\php\access\ShadowAccess;
|
||||||
use nur\sery\wip\php\access\ShadowAccess;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FormInput: accès à des paramètres de formulaire (POST ou GET, dans cet
|
* Class FormInput: accès à des paramètres de formulaire (POST ou GET, dans cet
|
||||||
@ -18,15 +17,14 @@ use nur\sery\wip\php\access\ShadowAccess;
|
|||||||
class FormInput extends Input {
|
class FormInput extends Input {
|
||||||
const ALLOW_EMPTY = false;
|
const ALLOW_EMPTY = false;
|
||||||
|
|
||||||
protected function formAccess($key): IAccess {
|
function __construct(&$dest=null, ?array $params=null) {
|
||||||
return new FormAccess($key, [
|
parent::__construct($dest, $params);
|
||||||
"allow_empty" => $this->allowEmpty,
|
$this->access = new ShadowAccess($this->formAccess($this->access), $this->access);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function access($key): IAccess {
|
protected function formAccess(IAccess $access): IAccess {
|
||||||
return $this->keyAccess[$key] ??= new ShadowAccess($this->formAccess($key), new KeyAccess($this->dest, $key, [
|
return new FormAccess(null, [
|
||||||
"allow_empty" => $this->allowEmpty,
|
"allow_empty" => $access->isAllowEmpty(),
|
||||||
]));
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\input;
|
namespace nulib\schema\input;
|
||||||
|
|
||||||
use nur\sery\wip\php\access\GetAccess;
|
use nulib\php\access\GetAccess;
|
||||||
use nur\sery\wip\php\access\IAccess;
|
use nulib\php\access\IAccess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class GetInput: accès à des paramètres de formulaire de type GET uniquement
|
* Class GetInput: accès à des paramètres de formulaire de type GET uniquement
|
||||||
@ -11,9 +11,9 @@ use nur\sery\wip\php\access\IAccess;
|
|||||||
* une référence
|
* une référence
|
||||||
*/
|
*/
|
||||||
class GetInput extends FormInput {
|
class GetInput extends FormInput {
|
||||||
protected function formAccess($key): IAccess {
|
protected function formAccess(IAccess $access): IAccess {
|
||||||
return new GetAccess($key, [
|
return new GetAccess(null, [
|
||||||
"allow_empty" => $this->allowEmpty,
|
"allow_empty" => $access->isAllowEmpty(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\input;
|
namespace nulib\schema\input;
|
||||||
|
|
||||||
use nur\sery\wip\php\access\IAccess;
|
use nulib\ref\schema\ref_input;
|
||||||
use nur\sery\wip\php\access\KeyAccess;
|
use nulib\StateException;
|
||||||
use nur\sery\wip\php\access\ValueAccess;
|
use nulib\php\access\IAccess;
|
||||||
|
use nulib\php\access\KeyAccess;
|
||||||
|
use nulib\php\access\PropertyAccess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Input: accès à une valeur
|
* Class Input: accès à une valeur
|
||||||
@ -13,54 +15,72 @@ use nur\sery\wip\php\access\ValueAccess;
|
|||||||
class Input {
|
class Input {
|
||||||
const ALLOW_EMPTY = true;
|
const ALLOW_EMPTY = true;
|
||||||
|
|
||||||
function __construct(&$dest=null, ?array $params=null) {
|
private static function unexpected_access_type(): StateException {
|
||||||
$this->dest =& $dest;
|
return StateException::unexpected_state("access_type");
|
||||||
$this->allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var mixed */
|
function __construct(&$dest=null, ?array $params=null) {
|
||||||
protected $dest;
|
$accessType = $params["access_type"] ?? ref_input::ACCESS_AUTO;
|
||||||
|
if ($accessType === ref_input::ACCESS_AUTO) {
|
||||||
|
$accessType = is_object($dest)? ref_input::ACCESS_PROPERTY : ref_input::ACCESS_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
$allowEmpty = $params["allow_empty"] ?? static::ALLOW_EMPTY;
|
||||||
* @var bool comment considérer une chaine vide: "" si allowEmpty, null sinon
|
if ($accessType == ref_input::ACCESS_PROPERTY) {
|
||||||
*/
|
$this->access = new PropertyAccess($dest, null, [
|
||||||
protected $allowEmpty;
|
"allow_empty" => $allowEmpty,
|
||||||
|
"allow_null" => true,
|
||||||
protected ?ValueAccess $valueAccess = null;
|
]);
|
||||||
protected ?array $keyAccess = null;
|
} elseif ($accessType == ref_input::ACCESS_KEY) {
|
||||||
|
$this->access = new KeyAccess($dest, null, [
|
||||||
protected function access($key): IAccess {
|
"allow_empty" => $allowEmpty,
|
||||||
if ($key === null) {
|
|
||||||
return $this->valueAccess ??= new ValueAccess($this->dest, [
|
|
||||||
"allow_null" => true,
|
"allow_null" => true,
|
||||||
"allow_empty" => $this->allowEmpty,
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
return $this->keyAccess[$key] ??= new KeyAccess($this->dest, $key, [
|
throw self::unexpected_access_type();
|
||||||
"allow_empty" => $this->allowEmpty,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected IAccess $access;
|
||||||
|
|
||||||
/** tester si la valeur existe sans tenir compte de $allowEmpty */
|
/** tester si la valeur existe sans tenir compte de $allowEmpty */
|
||||||
function isPresent($key=null): bool {
|
function isPresent($key=null): bool {
|
||||||
return $this->access($key)->exists();
|
return $this->access->resetKey($key)->exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** tester si la valeur est disponible en tenant compte de $allowEmpty */
|
/** tester si la valeur est disponible en tenant compte de $allowEmpty */
|
||||||
function isAvailable($key=null): bool {
|
function isAvailable($key=null): bool {
|
||||||
return $this->access($key)->available();
|
return $this->access->resetKey($key)->available();
|
||||||
}
|
}
|
||||||
|
|
||||||
function get($key=null) {
|
function get($key=null) {
|
||||||
return $this->access($key)->get();
|
return $this->access->resetKey($key)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
function set($value, $key=null): void {
|
function set($value, $key=null): void {
|
||||||
$this->access($key)->set($value);
|
$this->access->resetKey($key)->set($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unset($key=null): void {
|
function unset($key=null): void {
|
||||||
$this->access($key)->del();
|
$this->access->resetKey($key)->del();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKey($key): Input {
|
||||||
|
if ($key === null) return $this;
|
||||||
|
$input = clone $this;
|
||||||
|
$input->access = $this->access->addKey($key);
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAssoc(array $keys, ?array $params=null): void {
|
||||||
|
$this->access->ensureAssoc($keys, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureKeys(array $defaults, ?array $params=null): void {
|
||||||
|
$this->access->ensureKeys($defaults, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureOrder(array $keys, ?array $params=null): void {
|
||||||
|
$this->access->ensureOrder($keys, $params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\input;
|
namespace nulib\schema\input;
|
||||||
|
|
||||||
use nur\sery\wip\php\access\IAccess;
|
use nulib\php\access\IAccess;
|
||||||
use nur\sery\wip\php\access\PostAccess;
|
use nulib\php\access\PostAccess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class PostInput: accès à des paramètres de formulaire de type POST uniquement
|
* Class PostInput: accès à des paramètres de formulaire de type POST uniquement
|
||||||
@ -11,9 +11,9 @@ use nur\sery\wip\php\access\PostAccess;
|
|||||||
* une référence
|
* une référence
|
||||||
*/
|
*/
|
||||||
class PostInput extends FormInput {
|
class PostInput extends FormInput {
|
||||||
protected function formAccess($key): IAccess {
|
protected function formAccess(IAccess $access): IAccess {
|
||||||
return new PostAccess($key, [
|
return new PostAccess(null, [
|
||||||
"allow_empty" => $this->allowEmpty,
|
"allow_empty" => $access->isAllowEmpty(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema;
|
namespace nulib\schema;
|
||||||
|
|
||||||
use nur\sery\wip\schema\types\IType;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\types\Registry;
|
use nulib\schema\types\IType;
|
||||||
use nur\sery\wip\schema\types\tarray;
|
use nulib\schema\types\Registry;
|
||||||
use nur\sery\wip\schema\types\tbool;
|
use nulib\schema\types\tarray;
|
||||||
use nur\sery\wip\schema\types\tcallable;
|
use nulib\schema\types\tbool;
|
||||||
use nur\sery\wip\schema\types\tfloat;
|
use nulib\schema\types\tfunc;
|
||||||
use nur\sery\wip\schema\types\tint;
|
use nulib\schema\types\tcontent;
|
||||||
use nur\sery\wip\schema\types\tstring;
|
use nulib\schema\types\tfloat;
|
||||||
|
use nulib\schema\types\tint;
|
||||||
|
use nulib\schema\types\tkey;
|
||||||
|
use nulib\schema\types\tmixed;
|
||||||
|
use nulib\schema\types\tpkey;
|
||||||
|
use nulib\schema\types\traw;
|
||||||
|
use nulib\schema\types\trawstring;
|
||||||
|
use nulib\schema\types\tstring;
|
||||||
|
use nulib\schema\types\ttext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class types: classe outil pour gérer le registre de types
|
* Class types: classe outil pour gérer le registre de types
|
||||||
@ -24,14 +32,25 @@ class types {
|
|||||||
return self::$registry;
|
return self::$registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function get(string $name): IType {
|
static function get(bool $nullable, $name, ?array $args=null, ?array $definition=null): IType {
|
||||||
return self::registry()->get($name);
|
if ($name instanceof IType) return $name;
|
||||||
|
if ($name !== null && !is_string($name)) {
|
||||||
|
throw ValueException::invalid_type($name, "string");
|
||||||
|
}
|
||||||
|
return self::registry()->get($nullable, $name, $args, $definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function string(): tstring { return self::get("string"); }
|
static function rawstring(bool $nullable=true): trawstring { return self::get($nullable, "rawstring"); }
|
||||||
static function bool(): tbool { return self::get("bool"); }
|
static function string(bool $nullable=true): tstring { return self::get($nullable, "string"); }
|
||||||
static function int(): tint { return self::get("int"); }
|
static function text(bool $nullable=true): ttext { return self::get($nullable, "text"); }
|
||||||
static function float(): tfloat { return self::get("float"); }
|
static function bool(bool $nullable=true): tbool { return self::get($nullable, "bool"); }
|
||||||
static function array(): tarray { return self::get("array"); }
|
static function int(bool $nullable=true): tint { return self::get($nullable, "int"); }
|
||||||
static function callable(): tcallable { return self::get("callable"); }
|
static function float(bool $nullable=true): tfloat { return self::get($nullable, "float"); }
|
||||||
|
static function array(bool $nullable=true): tarray { return self::get($nullable, "array"); }
|
||||||
|
static function callable(bool $nullable=true): tfunc { return self::get($nullable, "callable"); }
|
||||||
|
static function raw(bool $nullable=true): traw { return self::get($nullable, "raw"); }
|
||||||
|
static function mixed(bool $nullable=true): tmixed { return self::get($nullable, "mixed"); }
|
||||||
|
static function key(bool $nullable=true): tkey { return self::get($nullable, "key"); }
|
||||||
|
static function pkey(bool $nullable=true): tpkey { return self::get($nullable, "pkey"); }
|
||||||
|
static function content(bool $nullable=true): tcontent { return self::get($nullable, "content"); }
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,147 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\input\Input;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\input\Input;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface IType: un type de données
|
* Interface IType: un type de données
|
||||||
*/
|
*/
|
||||||
interface IType {
|
interface IType {
|
||||||
/** la donnée $input($destKey) est-elle disponible? */
|
static function get_params_from_definition(?array $definition): ?array;
|
||||||
function isAvailable(Input $input, $destKey): bool;
|
|
||||||
|
/**
|
||||||
|
* obtenir, pour information, le nom officiel de ce type, utilisable dans une
|
||||||
|
* définition de schéma
|
||||||
|
*/
|
||||||
|
function getName(): string;
|
||||||
|
|
||||||
|
/** obtenir la liste des aliases valides pour ce type */
|
||||||
|
function getAliases(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string la classe des objets gérés par ce format: le type attendu
|
||||||
|
* par {@link format()} et le type retourné par {@link normalize()}
|
||||||
|
*
|
||||||
|
* Les valeurs "mixed", "bool", "float", "int", "string" et "array" peuvent
|
||||||
|
* aussi être retournées, bien qu'elles ne soient pas à proprement parler des
|
||||||
|
* classes.
|
||||||
|
*
|
||||||
|
* La valeur "mixed" signifie qu'il peut s'agir de n'importe quelle classe
|
||||||
|
* et/ou que la valeur ne peut pas être déterminée à l'avance.
|
||||||
|
*/
|
||||||
|
function getClass(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* comme {@link getClass()} mais peut être utilisé directement comme type dans
|
||||||
|
* une déclaration PHP. notamment, si le type est nullable et que
|
||||||
|
* $allowNullable==true, il y a "?" devant le nom.
|
||||||
|
*
|
||||||
|
* Par exemple:
|
||||||
|
* - pour un type chaine nullable, {@link getClass()} retournerait "string"
|
||||||
|
* alors que cette méthode retournerait "?string".
|
||||||
|
* - pour un type mixed, {@link getClass()} retournerait "mixed" alors que
|
||||||
|
* cette méthode retournerait null
|
||||||
|
*/
|
||||||
|
function getPhpType(bool $allowNullable=true): ?string;
|
||||||
|
|
||||||
|
/** obtenir la valeur "nulle" pour les objets de ce type */
|
||||||
|
function getNullValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* indiquer si c'est le type d'une valeur qui ne peut prendre que 2 états: une
|
||||||
|
* "vraie" et une "fausse"
|
||||||
|
*/
|
||||||
|
function is2States(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Si {@link is2States()} est vrai, retourner les deux valeurs [faux, vrai]
|
||||||
|
*/
|
||||||
|
function get2States(): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* indiquer si c'est le type d'une valeur qui ne peut prendre que 3 états: une
|
||||||
|
* "vraie", une "fausse", et une "indéterminée"
|
||||||
|
*/
|
||||||
|
function is3States(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Si {@link is3States()} est vrai, retourner les 3 valeurs [faux, vrai, undef]
|
||||||
|
*/
|
||||||
|
function get3States(): array;
|
||||||
|
|
||||||
|
/** la donnée $input($valueKey) est-elle disponible? */
|
||||||
|
function isAvailable(Input $input, $valueKey): bool;
|
||||||
|
|
||||||
/** la valeur $value est-elle nulle? */
|
/** la valeur $value est-elle nulle? */
|
||||||
function isNull($value): bool;
|
function isNull($value): bool;
|
||||||
|
|
||||||
/** la valeur $value est-elle valide et normalisée le cas échéant? */
|
/**
|
||||||
|
* la valeur $value est-elle valide et normalisée le cas échéant?
|
||||||
|
*
|
||||||
|
* NB: si $value est un string. elle doit avoir déjà été traitée au préalable
|
||||||
|
* par extract() et parse()
|
||||||
|
*/
|
||||||
function isValid($value, ?bool &$normalized=null): bool;
|
function isValid($value, ?bool &$normalized=null): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* analyser, corriger éventuellement et normaliser la valeur
|
* extraire de la chaine la valeur à analyser
|
||||||
*
|
*
|
||||||
* si la valeur était déjà normalisée, ou si une erreur s'est produite,
|
* @throws ValueException en cas d'erreur d'analyse
|
||||||
* retourner false.
|
|
||||||
*/
|
*/
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool;
|
function extract(string $value): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* formatter la valeur pour affichage. $value est garanti d'être du bon type
|
* analyser la chaine et retourner la valeur "convertie"
|
||||||
|
*
|
||||||
|
* @throws ValueException en cas d'erreur d'analyse
|
||||||
|
*/
|
||||||
|
function parse(string $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* normaliser la valeur. elle *doit* déjà être valide.
|
||||||
|
* Si $value est un string. elle *doit* avoir déjà été traitée au préalable
|
||||||
|
* par extract() et parse()
|
||||||
|
*
|
||||||
|
* - si $result indique que la valeur est déjà normalisée, cette méthode ne
|
||||||
|
* fait rien
|
||||||
|
* - si la valeur était déjà normalisée, mettre à jour $result pour indiquer
|
||||||
|
* que la valeur est normalisée et retourner false
|
||||||
|
* - sinon, retourner true pour indiquer qu'il a fallut normaliser la valeur.
|
||||||
|
* $result n'est pas modifié
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* formatter la valeur pour affichage. si $value n'est pas null, elle est
|
||||||
|
* garantie d'être du bon type
|
||||||
*/
|
*/
|
||||||
function format($value, $format=null): string;
|
function format($value, $format=null): string;
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
/** @return string le nom d'un getter pour une valeur de ce type */
|
||||||
|
function getGetterName(string $name): string;
|
||||||
|
|
||||||
|
/** @return string le nom d'un setter pour une valeur de ce type */
|
||||||
|
function getSetterName(string $name): string;
|
||||||
|
|
||||||
|
/** @return string le nom d'un deleter pour une valeur de ce type */
|
||||||
|
function getDeleterName(string $name): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string le nom d'une constante de classe pour une valeur de ce type
|
||||||
|
*/
|
||||||
|
function getClassConstName(string $name): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string le nom d'une propriété d'une classe pour une valeur de ce
|
||||||
|
* type
|
||||||
|
*/
|
||||||
|
function getObjectPropertyName(string $name): string;
|
||||||
|
|
||||||
|
/** @return string le nom d'une clé d'un tableau pour une valeur de ce type */
|
||||||
|
function getArrayKeyName(string $name): string;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
|
use nulib\php\func;
|
||||||
|
use nulib\ValueException;
|
||||||
|
|
||||||
class Registry {
|
class Registry {
|
||||||
const TYPES = [
|
const TYPES = [
|
||||||
|
"rawstring" => trawstring::class,
|
||||||
"string" => tstring::class,
|
"string" => tstring::class,
|
||||||
|
"text" => ttext::class,
|
||||||
"bool" => tbool::class, "boolean" => tbool::class,
|
"bool" => tbool::class, "boolean" => tbool::class,
|
||||||
"int" => tint::class, "integer" => tint::class,
|
"int" => tint::class, "integer" => tint::class,
|
||||||
"float" => tfloat::class, "flt" => tfloat::class,
|
"float" => tfloat::class, "flt" => tfloat::class, "double" => tfloat::class, "dbl" => tfloat::class,
|
||||||
"double" => tfloat::class, "dbl" => tfloat::class,
|
|
||||||
"array" => tarray::class,
|
"array" => tarray::class,
|
||||||
"callable" => tcallable::class,
|
"func" => tfunc::class, "function" => tfunc::class, "callable" => tfunc::class,
|
||||||
# types spéciaux
|
# types spéciaux
|
||||||
|
"raw" => traw::class,
|
||||||
|
"mixed" => tmixed::class,
|
||||||
"key" => tkey::class,
|
"key" => tkey::class,
|
||||||
"pkey" => tpkey::class,
|
"pkey" => tpkey::class,
|
||||||
"content" => tcontent::class,
|
"content" => tcontent::class,
|
||||||
@ -25,12 +30,33 @@ class Registry {
|
|||||||
/** @var IType[] */
|
/** @var IType[] */
|
||||||
protected $types;
|
protected $types;
|
||||||
|
|
||||||
function get(string $name): IType {
|
function get(bool $nullable, ?string $name, ?array $args=null, ?array $definition=null): IType {
|
||||||
$type = cl::get($this->types, $name);
|
if (cl::is_list($args)) {
|
||||||
if ($type === null) {
|
$key = array_key_last($args);
|
||||||
$class = self::TYPES[$name];
|
$params = $args[$key];
|
||||||
$type = $this->types[$name] = new $class();
|
unset($args[$key]);
|
||||||
|
} else {
|
||||||
|
$params = $args;
|
||||||
|
$args = null;
|
||||||
}
|
}
|
||||||
|
$name ??= "raw";
|
||||||
|
$class = cl::get(self::TYPES, $name);
|
||||||
|
if ($class === null) {
|
||||||
|
$class = $name;
|
||||||
|
if (!class_exists($class)) {
|
||||||
|
throw ValueException::invalid_type($class, IType::class);
|
||||||
|
} elseif (!is_subclass_of($class, IType::class)) {
|
||||||
|
return new tgeneric($class, $nullable, $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$params = cl::merge($class::get_params_from_definition($definition), $params);
|
||||||
|
if ($args || $params !== null) {
|
||||||
|
$args ??= [];
|
||||||
|
return func::with([$class, false, ...$args, $nullable, $params])->invoke();
|
||||||
|
}
|
||||||
|
if ($nullable) $name = "?$name";
|
||||||
|
$type = cl::get($this->types, $name);
|
||||||
|
if ($type === null) $type = $this->types[$name] = new $class($nullable);
|
||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
src/schema/types/_tformatable.php
Normal file
19
src/schema/types/_tformatable.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
abstract class _tformatable extends _tsimple {
|
||||||
|
const FORMAT = null;
|
||||||
|
|
||||||
|
static function get_params_from_definition(?array $definition): ?array {
|
||||||
|
$params = null;
|
||||||
|
$format = $definition["format"] ?? null;
|
||||||
|
if ($format !== null) $params["format"] = $format;
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format($value, $format=null): string {
|
||||||
|
$format ??= $this->params["format"] ?? static::FORMAT;
|
||||||
|
if ($format !== null) return sprintf($format, $value);
|
||||||
|
else return strval($value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,99 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\input\Input;
|
use nulib\StateException;
|
||||||
|
use nur\prop;
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
use nur\str;
|
||||||
|
|
||||||
abstract class _tsimple implements IType {
|
abstract class _tsimple implements IType {
|
||||||
function isAvailable(Input $input, $destKey): bool {
|
const NAME = null;
|
||||||
return $input->isAvailable($destKey) && $input->get($destKey) !== false;
|
|
||||||
|
const ALIASES = [];
|
||||||
|
|
||||||
|
static function get_params_from_definition(?array $definition): ?array {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $nullable et $params doivent toujours être les derniers arguments du
|
||||||
|
* constructeur
|
||||||
|
*/
|
||||||
|
function __construct(bool $nullable, ?array $params=null) {
|
||||||
|
$this->nullable = $nullable;
|
||||||
|
$this->params = $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool $nullable;
|
||||||
|
|
||||||
|
protected ?array $params;
|
||||||
|
|
||||||
|
function getName(): string {
|
||||||
|
return static::NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAliases(): array {
|
||||||
|
return static::ALIASES;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPhpType(bool $allowNullable=true): ?string {
|
||||||
|
$phpType = $this->getClass();
|
||||||
|
if ($phpType === "mixed") return null;
|
||||||
|
if ($this->nullable && $allowNullable) $phpType = "?$phpType";
|
||||||
|
return $phpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2States(): bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get2States(): array {
|
||||||
|
throw StateException::not_implemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
function is3States(): bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get3States(): array {
|
||||||
|
throw StateException::not_implemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAvailable(Input $input, $valueKey): bool {
|
||||||
|
return $input->isAvailable($valueKey) && $input->get($valueKey) !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNull($value): bool {
|
function isNull($value): bool {
|
||||||
return $value === null || (is_string($value) && trim($value) === "");
|
return $value === null || $value === "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract(string $value): string {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
function getGetterName(string $name): string {
|
||||||
|
return prop::get_getter_name($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSetterName(string $name): string {
|
||||||
|
return prop::get_setter_name($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeleterName(string $name): string {
|
||||||
|
return prop::get_deletter_name($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClassConstName(string $name): string {
|
||||||
|
return strtoupper($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObjectPropertyName(string $name): string {
|
||||||
|
return str::us2camel($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArrayKeyName(string $name): string {
|
||||||
|
return $name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
src/schema/types/_tstring.php
Normal file
26
src/schema/types/_tstring.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use nulib\str;
|
||||||
|
|
||||||
|
abstract class _tstring extends _tsimple {
|
||||||
|
/** @var bool faut-il trimmer la valeur */
|
||||||
|
const TRIM = false;
|
||||||
|
/** @var bool faut-il normaliser les caractères fin de ligne */
|
||||||
|
const NORM_NL = false;
|
||||||
|
|
||||||
|
static function get_params_from_definition(?array $definition): ?array {
|
||||||
|
$params = null;
|
||||||
|
$trim = $definition["trim"] ?? null;
|
||||||
|
if ($trim !== null) $params["trim"] = $trim;
|
||||||
|
$normNl = $definition["norm_nl"] ?? null;
|
||||||
|
if ($normNl !== null) $params["norm_nl"] = $normNl;
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract(string $value): string {
|
||||||
|
if ($this->params["trim"] ?? static::TRIM) $value = trim($value);
|
||||||
|
if ($this->params["norm_nl"] ?? static::NORM_NL) $value = str::norm_nl($value);
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
10
src/schema/types/_tunion.php
Normal file
10
src/schema/types/_tunion.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
abstract class _tunion extends _tsimple {
|
||||||
|
function getPhpType(bool $allowNullable=true): ?string {
|
||||||
|
# assumer mixed pour le moment
|
||||||
|
#XXX à terme, lister les types de l'union
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tarray extends _tstring {
|
||||||
|
const NAME = "array";
|
||||||
|
|
||||||
|
const SPLIT_PATTERN = '/\s+/';
|
||||||
|
const FORMAT = " ";
|
||||||
|
|
||||||
|
public static function get_params_from_definition(?array $definition): ?array {
|
||||||
|
$params = parent::get_params_from_definition($definition);
|
||||||
|
$splitPattern = $definition["split_pattern"] ?? null;
|
||||||
|
if ($splitPattern !== null) $params["split_pattern"] = $splitPattern;
|
||||||
|
$format = $definition["format"] ?? null;
|
||||||
|
if ($format !== null) $params["format"] = $format;
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
class tarray extends _tsimple {
|
|
||||||
static function ensure_array(&$array): void {
|
static function ensure_array(&$array): void {
|
||||||
if (!is_array($array)) $array = cl::with($array);
|
if (!is_array($array)) $array = cl::with($array);
|
||||||
}
|
}
|
||||||
@ -14,14 +31,42 @@ class tarray extends _tsimple {
|
|||||||
if ($array !== null) self::ensure_array($array);
|
if ($array !== null) self::ensure_array($array);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function getClass(): string {
|
||||||
$normalized = is_array($value);
|
return "array";
|
||||||
return is_scalar($value) || is_array($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = is_array($value);
|
||||||
|
return $normalized || is_scalar($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
$pattern = $this->params["split_pattern"] ?? static::SPLIT_PATTERN;
|
||||||
|
return preg_split($pattern, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if ($result->normalized) {
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
$result->setNormalized();
|
||||||
|
} elseif (is_scalar($value)) {
|
||||||
|
$value = cl::with($value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
function format($value, $format=null): string {
|
||||||
|
if ($value === null) return "";
|
||||||
|
$format ??= $this->params["format"] ?? static::FORMAT;
|
||||||
|
return implode($format, $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\cl;
|
use nulib\cl;
|
||||||
use nur\sery\ValueException;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\_scalar\ScalarResult;
|
use nur\prop;
|
||||||
use nur\sery\wip\schema\_scalar\ScalarSchema;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
use nur\sery\wip\schema\input\Input;
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\input\Input;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tbool extends _tformatable {
|
||||||
|
const NAME = "bool";
|
||||||
|
|
||||||
|
const ALIASES = ["boolean"];
|
||||||
|
|
||||||
class tbool extends _tsimple {
|
|
||||||
/** liste de valeurs chaines à considérer comme 'OUI' */
|
/** liste de valeurs chaines à considérer comme 'OUI' */
|
||||||
const YES_VALUES = [
|
const YES_VALUES = [
|
||||||
# IMPORTANT: ordonner par taille décroissante pour compatibilité avec parse()
|
|
||||||
"true", "vrai", "yes", "oui",
|
"true", "vrai", "yes", "oui",
|
||||||
"t", "v", "y", "o", "1",
|
"t", "v", "y", "o", "1",
|
||||||
];
|
];
|
||||||
|
|
||||||
/** liste de valeurs chaines à considérer comme 'NON' */
|
/** liste de valeurs chaines à considérer comme 'NON' */
|
||||||
const NO_VALUES = [
|
const NO_VALUES = [
|
||||||
# IMPORTANT: ordonner par taille décroissante pour compatibilité avec parse()
|
|
||||||
"false", "faux", "non", "no",
|
"false", "faux", "non", "no",
|
||||||
"f", "n", "0",
|
"f", "n", "0",
|
||||||
];
|
];
|
||||||
@ -53,41 +56,62 @@ class tbool extends _tsimple {
|
|||||||
if ($bool !== null) self::ensure_bool($bool);
|
if ($bool !== null) self::ensure_bool($bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAvailable(Input $input, $destKey): bool {
|
function getClass(): string {
|
||||||
return $input->isAvailable($destKey);
|
return "bool";
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2States(): bool {
|
||||||
|
return !$this->nullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get2States(): array {
|
||||||
|
return [false, true];
|
||||||
|
}
|
||||||
|
|
||||||
|
function is3States(): bool {
|
||||||
|
return $this->nullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get3States(): array {
|
||||||
|
return [false, true, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAvailable(Input $input, $valueKey): bool {
|
||||||
|
return $input->isAvailable($valueKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
$normalized = is_bool($value);
|
$normalized = is_bool($value);
|
||||||
if (is_string($value)) {
|
return is_scalar($value);
|
||||||
$value = trim($value);
|
}
|
||||||
$valid = self::is_yes($value) || self::is_no($value);
|
|
||||||
} else {
|
function extract(string $value): string {
|
||||||
$valid = is_scalar($value);
|
return trim($value);
|
||||||
}
|
}
|
||||||
return $valid;
|
|
||||||
|
function parse(string $value) {
|
||||||
|
if (self::is_yes($value)) return true;
|
||||||
|
elseif (self::is_no($value)) return false;
|
||||||
|
throw new ValueException("une valeur booléenne est attendue");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ScalarResult $result
|
* @var ScalarResult $result
|
||||||
* @var ScalarSchema $schema
|
* @var ScalarSchema $schema
|
||||||
*/
|
*/
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
if (is_bool($value)) {
|
if ($result->normalized) {
|
||||||
|
} elseif (is_bool($value)) {
|
||||||
$result->setNormalized();
|
$result->setNormalized();
|
||||||
return false;
|
|
||||||
} elseif (is_string($value)) {
|
|
||||||
$bool = trim($value);
|
|
||||||
if (self::is_yes($bool)) $value = true;
|
|
||||||
elseif (self::is_no($bool)) $value = false;
|
|
||||||
else return $result->setInvalid($value, $schema);
|
|
||||||
} elseif (is_scalar($value)) {
|
} elseif (is_scalar($value)) {
|
||||||
$value = boolval($value);
|
$value = boolval($value);
|
||||||
} else {
|
return true;
|
||||||
return $result->setInvalid($value, $schema);
|
|
||||||
}
|
}
|
||||||
$result->setValid();
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const OUINON_FORMAT = ["Oui", "Non", false];
|
const OUINON_FORMAT = ["Oui", "Non", false];
|
||||||
@ -105,10 +129,10 @@ class tbool extends _tsimple {
|
|||||||
"oz" => self::OZ_FORMAT,
|
"oz" => self::OZ_FORMAT,
|
||||||
];
|
];
|
||||||
|
|
||||||
const DEFAULT_FORMAT = self::OUINON_FORMAT;
|
const FORMAT = self::OUINON_FORMAT;
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
function format($value, $format=null): string {
|
||||||
if ($format === null) $format = static::DEFAULT_FORMAT;
|
$format ??= $this->params["format"] ?? static::FORMAT;
|
||||||
if (is_string($format)) {
|
if (is_string($format)) {
|
||||||
$oformat = $format;
|
$oformat = $format;
|
||||||
$format = cl::get(self::FORMATS, strtolower($oformat));
|
$format = cl::get(self::FORMATS, strtolower($oformat));
|
||||||
@ -120,4 +144,8 @@ class tbool extends _tsimple {
|
|||||||
}
|
}
|
||||||
return $value? $format[0]: $format[1];
|
return $value? $format[0]: $format[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGetterName(string $name): string {
|
||||||
|
return prop::get_getter_name($name, !$this->nullable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\wip\schema\types;
|
|
||||||
|
|
||||||
use nur\sery\php\nur_func;
|
|
||||||
use nur\sery\ValueException;
|
|
||||||
use nur\sery\wip\schema\Result;
|
|
||||||
use nur\sery\wip\schema\Schema;
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
class tcallable extends _tsimple {
|
|
||||||
static function ensure_callable(&$callable): void {
|
|
||||||
if (!is_callable($callable)) throw ValueException::invalid_type($callable, "callable");
|
|
||||||
}
|
|
||||||
|
|
||||||
static function ensure_ncallable(&$callable): void {
|
|
||||||
if ($callable !== null) self::ensure_callable($callable);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
|
||||||
$normalized = is_callable($value);
|
|
||||||
return nur_func::check_func($value, stdClass::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
|
||||||
}
|
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +1,57 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\php\content\c;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
abstract class tcontent extends _tunion {
|
||||||
|
const NAME = "content";
|
||||||
|
|
||||||
abstract class tcontent extends _tsimple {
|
|
||||||
static function ensure_content(&$content): void {
|
static function ensure_content(&$content): void {
|
||||||
if (!is_string($content) && !is_array($content)) $content = strval($content);
|
if ($content === null || $content === false) $content = [];
|
||||||
|
elseif (!is_string($content) && !is_array($content)) $content = strval($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function ensure_ncontent(&$content): void {
|
static function ensure_ncontent(&$content): void {
|
||||||
if ($content !== null) self::ensure_content($content);
|
if ($content !== null) self::ensure_content($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function getClass(): string {
|
||||||
$normalized = is_string($value) || is_array($value);
|
return "string|array";
|
||||||
return is_scalar($value) || is_array($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = is_string($value) || is_array($value);
|
||||||
|
return $normalized || is_scalar($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if ($result->normalized) {
|
||||||
|
} elseif (is_string($value) || is_array($value)) {
|
||||||
|
$result->setNormalized();
|
||||||
|
} elseif (is_scalar($value)) {
|
||||||
|
$value = strval($value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
function format($value, $format=null): string {
|
||||||
|
return c::to_string($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\_scalar\ScalarResult;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\_scalar\ScalarSchema;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tfloat extends _tformatable {
|
||||||
|
const NAME = "float";
|
||||||
|
|
||||||
|
const ALIASES = ["flt", "double", "dbl"];
|
||||||
|
|
||||||
class tfloat extends _tsimple {
|
|
||||||
static function ensure_float(&$float): void {
|
static function ensure_float(&$float): void {
|
||||||
if (!is_float($float)) $float = floatval($float);
|
if (!is_float($float)) $float = floatval($float);
|
||||||
}
|
}
|
||||||
@ -15,36 +20,41 @@ class tfloat extends _tsimple {
|
|||||||
if ($float !== null) self::ensure_float($float);
|
if ($float !== null) self::ensure_float($float);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getClass(): string {
|
||||||
|
return "float";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
$normalized = is_float($value);
|
$normalized = is_float($value);
|
||||||
if (is_string($value)) $valid = is_numeric(trim($value));
|
return is_scalar($value);
|
||||||
else $valid = is_scalar($value);
|
}
|
||||||
return $valid;
|
|
||||||
|
function extract(string $value): string {
|
||||||
|
return trim($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
$value = str_replace(",", ".", trim($value));
|
||||||
|
if (is_numeric($value)) return floatval($value);
|
||||||
|
throw new ValueException("une valeur numérique flottante est attendue");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ScalarResult $result
|
* @var ScalarResult $result
|
||||||
* @var ScalarSchema $schema
|
* @var ScalarSchema $schema
|
||||||
*/
|
*/
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
if (is_float($value)) {
|
if ($result->normalized) {
|
||||||
|
} elseif (is_float($value)) {
|
||||||
$result->setNormalized();
|
$result->setNormalized();
|
||||||
return false;
|
|
||||||
} elseif (is_string($value)) {
|
|
||||||
$float = trim($value);
|
|
||||||
if (is_numeric($float)) $value = floatval($float);
|
|
||||||
else return $result->setInvalid($value, $schema);
|
|
||||||
} elseif (is_scalar($value)) {
|
} elseif (is_scalar($value)) {
|
||||||
$value = floatval($value);
|
$value = floatval($value);
|
||||||
} else {
|
return true;
|
||||||
return $result->setInvalid($value, $schema);
|
|
||||||
}
|
}
|
||||||
$result->setValid();
|
return false;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
|
||||||
if ($format !== null) return sprintf($format, $value);
|
|
||||||
else return strval($value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
src/schema/types/tfunc.php
Normal file
63
src/schema/types/tfunc.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use nulib\php\func;
|
||||||
|
use nulib\ValueException;
|
||||||
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tfunc extends _tsimple {
|
||||||
|
const NAME = "func";
|
||||||
|
|
||||||
|
const ALIASES = ["function", "callable"];
|
||||||
|
|
||||||
|
static function ensure_func(&$func): void {
|
||||||
|
$func = func::ensure($func);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function ensure_nfunc(&$func): void {
|
||||||
|
if ($func !== null) self::ensure_func($func);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClass(): string {
|
||||||
|
return func::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = $value instanceof func;
|
||||||
|
return func::check($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
try {
|
||||||
|
return func::ensure($value);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new ValueException(null, null, 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if ($result->normalized) {
|
||||||
|
} elseif ($value instanceof func) {
|
||||||
|
$result->setNormalized();
|
||||||
|
} elseif (is_callable($value)) {
|
||||||
|
$value = func::with($value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format($value, $format=null): string {
|
||||||
|
}
|
||||||
|
}
|
56
src/schema/types/tgeneric.php
Normal file
56
src/schema/types/tgeneric.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use nulib\ValueException;
|
||||||
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tgeneric extends _tsimple {
|
||||||
|
function __construct(string $class, bool $nullable, ?array $params=null) {
|
||||||
|
$this->class = $class;
|
||||||
|
parent::__construct($nullable, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string $class;
|
||||||
|
|
||||||
|
function getClass(): string {
|
||||||
|
return $this->class;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAvailable(Input $input, $valueKey): bool {
|
||||||
|
return $input->isAvailable($valueKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isNull($value): bool {
|
||||||
|
return $value === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = $value instanceof $this->class;
|
||||||
|
return $normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
throw ValueException::invalid_type($value, $this->class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if (!$result->normalized) $result->setNormalized();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format($value, $format=null): string {
|
||||||
|
return strval($value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\_scalar\ScalarResult;
|
use nulib\ValueException;
|
||||||
use nur\sery\wip\schema\_scalar\ScalarSchema;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tint extends _tformatable {
|
||||||
|
const NAME = "int";
|
||||||
|
|
||||||
|
const ALIASES = ["integer"];
|
||||||
|
|
||||||
class tint extends _tsimple {
|
|
||||||
static function ensure_int(&$int): void {
|
static function ensure_int(&$int): void {
|
||||||
if (!is_int($int)) $int = intval($int);
|
if (!is_int($int)) $int = intval($int);
|
||||||
}
|
}
|
||||||
@ -15,38 +20,43 @@ class tint extends _tsimple {
|
|||||||
if ($int !== null) self::ensure_int($int);
|
if ($int !== null) self::ensure_int($int);
|
||||||
}
|
}
|
||||||
|
|
||||||
const INT_PATTERN = '/^[-+]?[0-9]+(?:\.[0-9]*)?$/';
|
//const INT_PATTERN = '/^[-+]?[0-9]+(?:\.[0-9]*)?$/';
|
||||||
|
|
||||||
|
function getClass(): string {
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: 0;
|
||||||
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
$normalized = is_int($value);
|
$normalized = is_int($value);
|
||||||
if (is_string($value)) $valid = is_numeric(trim($value));
|
return is_scalar($value);
|
||||||
else $valid = is_scalar($value);
|
}
|
||||||
return $valid;
|
|
||||||
|
function extract(string $value): string {
|
||||||
|
return trim($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
$value = str_replace(",", ".", trim($value));
|
||||||
|
if (is_numeric($value)) return intval($value);
|
||||||
|
throw new ValueException("une valeur numérique entière est attendue");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ScalarResult $result
|
* @var ScalarResult $result
|
||||||
* @var ScalarSchema $schema
|
* @var ScalarSchema $schema
|
||||||
*/
|
*/
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
if (is_int($value)) {
|
if ($result->normalized) {
|
||||||
|
} elseif (is_int($value)) {
|
||||||
$result->setNormalized();
|
$result->setNormalized();
|
||||||
return false;
|
|
||||||
} elseif (is_string($value)) {
|
|
||||||
$int = trim($value);
|
|
||||||
if (is_numeric($int)) $value = intval($int);
|
|
||||||
else return $result->setInvalid($value, $schema);
|
|
||||||
} elseif (is_scalar($value)) {
|
} elseif (is_scalar($value)) {
|
||||||
$value = intval($value);
|
$value = intval($value);
|
||||||
} else {
|
return true;
|
||||||
return $result->setInvalid($value, $schema);
|
|
||||||
}
|
}
|
||||||
$result->setValid();
|
return false;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
|
||||||
if ($format !== null) return sprintf($format, $value);
|
|
||||||
else return strval($value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,57 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tkey extends _tunion {
|
||||||
|
const NAME = "key";
|
||||||
|
|
||||||
class tkey extends _tsimple {
|
|
||||||
static function ensure_key(&$key): void {
|
static function ensure_key(&$key): void {
|
||||||
if (!is_string($key) && !is_int($key)) $key = strval($key);
|
if ($key === null) $key = "";
|
||||||
|
elseif ($key === false) $key = 0;
|
||||||
|
elseif (!is_string($key) && !is_int($key)) $key = strval($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function ensure_nkey(&$key): void {
|
static function ensure_nkey(&$key): void {
|
||||||
if ($key !== null) self::ensure_key($key);
|
if ($key !== null) self::ensure_key($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function getClass(): string {
|
||||||
$normalized = is_string($value) || is_int($value);
|
return "string|int";
|
||||||
return is_scalar($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = is_string($value) || is_int($value);
|
||||||
|
return $normalized || is_scalar($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if ($result->normalized) {
|
||||||
|
} elseif (is_string($value) || is_int($value)) {
|
||||||
|
$result->setNormalized();
|
||||||
|
} elseif (is_scalar($value)) {
|
||||||
|
$value = strval($value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
function format($value, $format=null): string {
|
||||||
|
return strval($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
src/schema/types/tmixed.php
Normal file
50
src/schema/types/tmixed.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tmixed extends _tsimple {
|
||||||
|
const NAME = "mixed";
|
||||||
|
|
||||||
|
function getClass(): string {
|
||||||
|
return "mixed";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAvailable(Input $input, $valueKey): bool {
|
||||||
|
return $input->isAvailable($valueKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isNull($value): bool {
|
||||||
|
return $value === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if (!$result->normalized) $result->setNormalized();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format($value, $format=null): string {
|
||||||
|
return strval($value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\Result;
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
use nur\sery\wip\schema\Schema;
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class tpkey extends _tunion {
|
||||||
|
const NAME = "pkey";
|
||||||
|
|
||||||
class tpkey extends _tsimple {
|
|
||||||
static function ensure_pkey(&$pkey): void {
|
static function ensure_pkey(&$pkey): void {
|
||||||
if (!is_string($pkey) && !is_int($pkey) && !is_array($pkey)) $pkey = strval($pkey);
|
if ($pkey === null) $pkey = "";
|
||||||
|
elseif ($pkey === false) $pkey = 0;
|
||||||
|
elseif (!is_string($pkey) && !is_int($pkey) && !is_array($pkey)) $pkey = strval($pkey);
|
||||||
if (is_array($pkey)) {
|
if (is_array($pkey)) {
|
||||||
foreach ($pkey as &$key) {
|
foreach ($pkey as &$key) {
|
||||||
tkey::ensure_key($key);
|
tkey::ensure_key($key);
|
||||||
};
|
}; unset($key);
|
||||||
unset($key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,14 +24,40 @@ class tpkey extends _tsimple {
|
|||||||
if ($pkey !== null) self::ensure_pkey($pkey);
|
if ($pkey !== null) self::ensure_pkey($pkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
function getClass(): string {
|
||||||
$normalized = is_string($value) || is_int($value) || is_array($value);
|
return "string|int|array";
|
||||||
return is_scalar($value) || is_array($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
$normalized = is_string($value) || is_int($value) || is_array($value);
|
||||||
|
return $normalized || is_scalar($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if ($result->normalized) {
|
||||||
|
} elseif (is_string($value) || is_int($value) || is_array($value)) {
|
||||||
|
$result->setNormalized();
|
||||||
|
} elseif (is_scalar($value)) {
|
||||||
|
$value = strval($value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
function format($value, $format=null): string {
|
||||||
|
if (is_array($value)) return implode(".", $value);
|
||||||
|
else return strval($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/schema/types/traw.php
Normal file
16
src/schema/types/traw.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use nulib\schema\input\Input;
|
||||||
|
|
||||||
|
class traw extends tmixed {
|
||||||
|
const NAME = "raw";
|
||||||
|
|
||||||
|
function isAvailable(Input $input, $valueKey): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isNull($value): bool {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
65
src/schema/types/trawstring.php
Normal file
65
src/schema/types/trawstring.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
use nulib\str;
|
||||||
|
use nulib\schema\_scalar\ScalarResult;
|
||||||
|
use nulib\schema\_scalar\ScalarSchema;
|
||||||
|
use nulib\schema\Result;
|
||||||
|
use nulib\schema\Schema;
|
||||||
|
|
||||||
|
class trawstring extends _tstring {
|
||||||
|
const NAME = "rawstring";
|
||||||
|
|
||||||
|
static function ensure_string(&$string): void {
|
||||||
|
if (!is_string($string)) $string = strval($string);
|
||||||
|
if (static::TRIM) $string = trim($string);
|
||||||
|
if (static::NORM_NL) $string = str::norm_nl($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function ensure_nstring(&$string): void {
|
||||||
|
if ($string !== null) self::ensure_string($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClass(): string {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullValue() {
|
||||||
|
return $this->nullable? null: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNull($value): bool {
|
||||||
|
return $value === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid($value, ?bool &$normalized=null): bool {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$normalized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return is_scalar($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(string $value) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScalarResult $result
|
||||||
|
* @var ScalarSchema $schema
|
||||||
|
*/
|
||||||
|
function normalize(&$value, Result $result, Schema $schema): bool {
|
||||||
|
if ($result->normalized) {
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
$result->setNormalized();
|
||||||
|
} elseif (is_scalar($value)) {
|
||||||
|
$value = strval($value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format($value, $format=null): string {
|
||||||
|
return strval($value);
|
||||||
|
}
|
||||||
|
}
|
@ -1,48 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\schema\types;
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
use nur\sery\wip\schema\_scalar\ScalarResult;
|
class tstring extends trawstring {
|
||||||
use nur\sery\wip\schema\_scalar\ScalarSchema;
|
const NAME = "string";
|
||||||
use nur\sery\wip\schema\Result;
|
|
||||||
use nur\sery\wip\schema\Schema;
|
|
||||||
|
|
||||||
class tstring extends _tsimple {
|
const TRIM = true;
|
||||||
static function ensure_string(&$string): void {
|
|
||||||
if (!is_string($string)) $string = strval($string);
|
|
||||||
}
|
|
||||||
|
|
||||||
static function ensure_nstring(&$string): void {
|
|
||||||
if ($string !== null) self::ensure_string($string);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNull($value): bool {
|
|
||||||
return $value === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValid($value, ?bool &$normalized=null): bool {
|
|
||||||
$normalized = is_string($value);
|
|
||||||
return is_scalar($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ScalarResult $result
|
|
||||||
* @var ScalarSchema $schema
|
|
||||||
*/
|
|
||||||
function verifix(&$value, Result &$result, Schema $schema): bool {
|
|
||||||
if (is_string($value)) {
|
|
||||||
$result->setNormalized();
|
|
||||||
return false;
|
|
||||||
} elseif (is_scalar($value)) {
|
|
||||||
$value = strval($value);
|
|
||||||
$result->setValid();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
$result->setInvalid($value, $schema);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function format($value, $format=null): string {
|
|
||||||
return strval($value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
9
src/schema/types/ttext.php
Normal file
9
src/schema/types/ttext.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
namespace nulib\schema\types;
|
||||||
|
|
||||||
|
class ttext extends trawstring {
|
||||||
|
const NAME = "text";
|
||||||
|
|
||||||
|
const TRIM = true;
|
||||||
|
const NORM_NL = true;
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\web\content;
|
namespace nulib\web\content;
|
||||||
|
|
||||||
use nur\sery\A;
|
|
||||||
use nur\sery\php\content\c;
|
use nulib\A;
|
||||||
use nur\sery\php\content\IContent;
|
use nulib\php\content\c;
|
||||||
|
use nulib\php\content\IContent;
|
||||||
|
|
||||||
class Tag implements IContent {
|
class Tag implements IContent {
|
||||||
function __construct(string $tag, $content=null) {
|
function __construct(string $tag, $content=null) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery\wip\web\content;
|
namespace nulib\web\content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class v: classe outil pour gérer du contenu pour le web
|
* Class v: classe outil pour gérer du contenu pour le web
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib;
|
|
||||||
|
|
||||||
class app extends \nur\sery\app {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nulib\app\cli;
|
|
||||||
|
|
||||||
abstract class Application extends \nur\sery\app\cli\Application {
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace nur\sery\app;
|
|
||||||
|
|
||||||
use nulib\tests\TestCase;
|
|
||||||
use nur\sery\app\args;
|
|
||||||
|
|
||||||
class argsTest extends TestCase {
|
|
||||||
function testFrom_array() {
|
|
||||||
self::assertSame([], args::from_array(null));
|
|
||||||
self::assertSame([], args::from_array([]));
|
|
||||||
self::assertSame([], args::from_array([false]));
|
|
||||||
self::assertSame(["x"], args::from_array(["x", false]));
|
|
||||||
|
|
||||||
self::assertSame(["--opt"], args::from_array(["--opt"]));
|
|
||||||
self::assertSame(["--opt", "value"], args::from_array(["--opt", "value"]));
|
|
||||||
|
|
||||||
self::assertSame([], args::from_array(["opt" => false]));
|
|
||||||
self::assertSame(["--opt"], args::from_array(["opt" => true]));
|
|
||||||
self::assertSame(["--opt", "value"], args::from_array(["opt" => "value"]));
|
|
||||||
self::assertSame(["--opt", "42"], args::from_array(["opt" => 42]));
|
|
||||||
self::assertSame(["--opt", "1", "2", "3", "--"], args::from_array(["opt" => [1, 2, 3]]));
|
|
||||||
|
|
||||||
self::assertSame(["x", "1", "2", "3", "y"], args::from_array(["x", [1, 2, 3], "y"]));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace nur\sery {
|
namespace nulib {
|
||||||
use nulib\tests\TestCase;
|
use nulib\tests\TestCase;
|
||||||
use nur\sery\impl\config;
|
use nulib\impl\config;
|
||||||
use nur\sery\impl\myapp;
|
use nulib\impl\myapp;
|
||||||
use nur\sery\impl\MyApplication1;
|
use nulib\impl\MyApplication1;
|
||||||
use nur\sery\impl\MyApplication2;
|
use nulib\impl\MyApplication2;
|
||||||
|
|
||||||
class appTest extends TestCase {
|
class appTest extends TestCase {
|
||||||
function testWith() {
|
function testWith() {
|
||||||
@ -97,11 +97,11 @@ namespace nur\sery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace nur\sery\impl {
|
namespace nulib\impl {
|
||||||
|
|
||||||
use nur\sery\app\cli\Application;
|
use nulib\app\cli\Application;
|
||||||
use nur\sery\os\path;
|
use nulib\os\path;
|
||||||
use nur\sery\app;
|
use nulib\app;
|
||||||
|
|
||||||
class config {
|
class config {
|
||||||
const PROJDIR = __DIR__.'/..';
|
const PROJDIR = __DIR__.'/..';
|
||||||
|
1
tests/db/sqlite/.gitignore
vendored
1
tests/db/sqlite/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/capacitor.db*
|
|
@ -1,344 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\db\sqlite;
|
|
||||||
|
|
||||||
use nulib\tests\TestCase;
|
|
||||||
use nur\sery\cl;
|
|
||||||
use nur\sery\db\Capacitor;
|
|
||||||
use nur\sery\db\CapacitorChannel;
|
|
||||||
|
|
||||||
class SqliteStorageTest extends TestCase {
|
|
||||||
function _testChargeStrings(SqliteStorage $storage, ?string $channel) {
|
|
||||||
$storage->reset($channel);
|
|
||||||
$storage->charge($channel, "first");
|
|
||||||
$storage->charge($channel, "second");
|
|
||||||
$storage->charge($channel, "third");
|
|
||||||
$items = cl::all($storage->discharge($channel, false));
|
|
||||||
self::assertSame(["first", "second", "third"], $items);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _testChargeArrays(SqliteStorage $storage, ?string $channel) {
|
|
||||||
$storage->reset($channel);
|
|
||||||
$storage->charge($channel, ["id" => 10, "name" => "first"]);
|
|
||||||
$storage->charge($channel, ["name" => "second", "id" => 20]);
|
|
||||||
$storage->charge($channel, ["name" => "third", "id" => "30"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testChargeStrings() {
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$this->_testChargeStrings($storage, null);
|
|
||||||
$storage->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testChargeArrays() {
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$storage->addChannel(new class extends CapacitorChannel {
|
|
||||||
const NAME = "arrays";
|
|
||||||
const COLUMN_DEFINITIONS = ["id" => "integer"];
|
|
||||||
|
|
||||||
function getItemValues($item): ?array {
|
|
||||||
return ["id" => $item["id"] ?? null];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->_testChargeStrings($storage, "strings");
|
|
||||||
$this->_testChargeArrays($storage, "arrays");
|
|
||||||
$storage->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testEach() {
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
|
|
||||||
const NAME = "each";
|
|
||||||
const COLUMN_DEFINITIONS = [
|
|
||||||
"age" => "integer",
|
|
||||||
"done" => "integer default 0",
|
|
||||||
];
|
|
||||||
|
|
||||||
function getItemValues($item): ?array {
|
|
||||||
return [
|
|
||||||
"age" => $item["age"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->reset();
|
|
||||||
$capacitor->charge(["name" => "first", "age" => 5]);
|
|
||||||
$capacitor->charge(["name" => "second", "age" => 10]);
|
|
||||||
$capacitor->charge(["name" => "third", "age" => 15]);
|
|
||||||
$capacitor->charge(["name" => "fourth", "age" => 20]);
|
|
||||||
|
|
||||||
$setDone = function ($item, $row, $suffix=null) {
|
|
||||||
$updates = ["done" => 1];
|
|
||||||
if ($suffix !== null) {
|
|
||||||
$item["name"] .= $suffix;
|
|
||||||
$updates["item"] = $item;
|
|
||||||
}
|
|
||||||
return $updates;
|
|
||||||
};
|
|
||||||
$capacitor->each(["age" => [">", 10]], $setDone, ["++"]);
|
|
||||||
$capacitor->each(["done" => 0], $setDone, null);
|
|
||||||
|
|
||||||
Txx(cl::all($capacitor->discharge(false)));
|
|
||||||
$capacitor->close();
|
|
||||||
self::assertTrue(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPrimayKey() {
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
|
|
||||||
const NAME = "pk";
|
|
||||||
const COLUMN_DEFINITIONS = [
|
|
||||||
"id_" => "varchar primary key",
|
|
||||||
"done" => "integer default 0",
|
|
||||||
];
|
|
||||||
|
|
||||||
function getItemValues($item): ?array {
|
|
||||||
return [
|
|
||||||
"id_" => $item["numero"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->charge(["numero" => "a", "name" => "first", "age" => 5]);
|
|
||||||
$capacitor->charge(["numero" => "b", "name" => "second", "age" => 10]);
|
|
||||||
$capacitor->charge(["numero" => "c", "name" => "third", "age" => 15]);
|
|
||||||
$capacitor->charge(["numero" => "d", "name" => "fourth", "age" => 20]);
|
|
||||||
sleep(2);
|
|
||||||
$capacitor->charge(["numero" => "b", "name" => "second", "age" => 100]);
|
|
||||||
$capacitor->charge(["numero" => "d", "name" => "fourth", "age" => 200]);
|
|
||||||
|
|
||||||
$capacitor->close();
|
|
||||||
self::assertTrue(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSum() {
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
|
|
||||||
const NAME = "sum";
|
|
||||||
const COLUMN_DEFINITIONS = [
|
|
||||||
"a__" => "varchar",
|
|
||||||
"b__" => "varchar",
|
|
||||||
"b__sum_" => self::SUM_DEFINITION,
|
|
||||||
];
|
|
||||||
|
|
||||||
function getItemValues($item): ?array {
|
|
||||||
return [
|
|
||||||
"a" => $item["a"],
|
|
||||||
"b" => $item["b"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->reset();
|
|
||||||
$capacitor->charge(["a" => null, "b" => null]);
|
|
||||||
$capacitor->charge(["a" => "first", "b" => "second"]);
|
|
||||||
|
|
||||||
Txx("=== all");
|
|
||||||
/** @var Sqlite $sqlite */
|
|
||||||
$sqlite = $capacitor->getStorage()->db();
|
|
||||||
Txx(cl::all($sqlite->all([
|
|
||||||
"select",
|
|
||||||
"from" => $capacitor->getChannel()->getTableName(),
|
|
||||||
])));
|
|
||||||
Txx("=== each");
|
|
||||||
$capacitor->each(null, function ($item, $values) {
|
|
||||||
Txx($values);
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->close();
|
|
||||||
self::assertTrue(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testEachValues() {
|
|
||||||
# tester que values contient bien toutes les valeurs de la ligne
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
|
|
||||||
const NAME = "each_values";
|
|
||||||
const COLUMN_DEFINITIONS = [
|
|
||||||
"name" => "varchar primary key",
|
|
||||||
"age" => "integer",
|
|
||||||
"done" => "integer default 0",
|
|
||||||
"notes" => "text",
|
|
||||||
];
|
|
||||||
|
|
||||||
function getItemValues($item): ?array {
|
|
||||||
return [
|
|
||||||
"name" => $item["name"],
|
|
||||||
"age" => $item["age"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->reset();
|
|
||||||
$capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame("first", $item["name"]);
|
|
||||||
self::assertSame(5, $item["age"]);
|
|
||||||
self::assertnotnull($values);
|
|
||||||
self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($values));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 5,
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($values, ["name", "age", "item"]));
|
|
||||||
self::assertNull($pvalues);
|
|
||||||
});
|
|
||||||
$capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame("first", $item["name"]);
|
|
||||||
self::assertSame(10, $item["age"]);
|
|
||||||
self::assertnotnull($values);
|
|
||||||
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 10,
|
|
||||||
"done" => 0,
|
|
||||||
"notes" => null,
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($values, ["name", "age", "done", "notes", "item"]));
|
|
||||||
self::assertNotNull($pvalues);
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 5,
|
|
||||||
"done" => 0,
|
|
||||||
"notes" => null,
|
|
||||||
"item" => ["name" => "first", "age" => 5],
|
|
||||||
], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->each(null, function($item, ?array $values) {
|
|
||||||
self::assertSame("first", $item["name"]);
|
|
||||||
self::assertSame(10, $item["age"]);
|
|
||||||
self::assertnotnull($values);
|
|
||||||
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 10,
|
|
||||||
"done" => 0,
|
|
||||||
"notes" => null,
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($values, ["name", "age", "done", "notes", "item"]));
|
|
||||||
return [
|
|
||||||
"done" => 1,
|
|
||||||
"notes" => "modified",
|
|
||||||
];
|
|
||||||
});
|
|
||||||
$capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame("first", $item["name"]);
|
|
||||||
self::assertSame(10, $item["age"]);
|
|
||||||
self::assertnotnull($values);
|
|
||||||
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 10,
|
|
||||||
"done" => 1,
|
|
||||||
"notes" => "modified",
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($values, ["name", "age", "done", "notes", "item"]));
|
|
||||||
self::assertNotNull($pvalues);
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 10,
|
|
||||||
"done" => 1,
|
|
||||||
"notes" => "modified",
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame("first", $item["name"]);
|
|
||||||
self::assertSame(20, $item["age"]);
|
|
||||||
self::assertnotnull($values);
|
|
||||||
self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 20,
|
|
||||||
"done" => 1,
|
|
||||||
"notes" => "modified",
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($values, ["name", "age", "done", "notes", "item"]));
|
|
||||||
self::assertNotNull($pvalues);
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first",
|
|
||||||
"age" => 10,
|
|
||||||
"done" => 1,
|
|
||||||
"notes" => "modified",
|
|
||||||
"item" => ["name" => "first", "age" => 10],
|
|
||||||
], cl::select($pvalues, ["name", "age", "done", "notes", "item"]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSetItemNull() {
|
|
||||||
# tester le forçage de $îtem à null pour économiser la place
|
|
||||||
$storage = new SqliteStorage(__DIR__.'/capacitor.db');
|
|
||||||
$capacitor = new Capacitor($storage, new class extends CapacitorChannel {
|
|
||||||
const NAME = "set_item_null";
|
|
||||||
const COLUMN_DEFINITIONS = [
|
|
||||||
"name" => "varchar primary key",
|
|
||||||
"age" => "integer",
|
|
||||||
"done" => "integer default 0",
|
|
||||||
"notes" => "text",
|
|
||||||
];
|
|
||||||
|
|
||||||
function getItemValues($item): ?array {
|
|
||||||
return [
|
|
||||||
"name" => $item["name"],
|
|
||||||
"age" => $item["age"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$capacitor->reset();
|
|
||||||
$nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 5,
|
|
||||||
"item" => $item,
|
|
||||||
], cl::select($values, ["name", "age", "item"]));
|
|
||||||
return ["item" => null];
|
|
||||||
});
|
|
||||||
self::assertSame(1, $nbModified);
|
|
||||||
sleep(1);
|
|
||||||
# nb: on met des sleep() pour que la date de modification soit systématiquement différente
|
|
||||||
|
|
||||||
$nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 10,
|
|
||||||
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
|
|
||||||
], cl::select($values, ["name", "age", "item", "item__sum_"]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 5,
|
|
||||||
"item" => null, "item__sum_" => null,
|
|
||||||
], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
|
|
||||||
return ["item" => null];
|
|
||||||
});
|
|
||||||
self::assertSame(1, $nbModified);
|
|
||||||
sleep(1);
|
|
||||||
|
|
||||||
# pas de modification ici
|
|
||||||
$nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 10,
|
|
||||||
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
|
|
||||||
], cl::select($values, ["name", "age", "item", "item__sum_"]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 10,
|
|
||||||
"item" => null, "item__sum_" => null,
|
|
||||||
], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
|
|
||||||
return ["item" => null];
|
|
||||||
});
|
|
||||||
self::assertSame(0, $nbModified);
|
|
||||||
sleep(1);
|
|
||||||
|
|
||||||
$nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $values, ?array $pvalues) {
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 20,
|
|
||||||
"item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5",
|
|
||||||
], cl::select($values, ["name", "age", "item", "item__sum_"]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "first", "age" => 10,
|
|
||||||
"item" => null, "item__sum_" => null,
|
|
||||||
], cl::select($pvalues, ["name", "age", "item", "item__sum_"]));
|
|
||||||
return ["item" => null];
|
|
||||||
});
|
|
||||||
self::assertSame(1, $nbModified);
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\db\sqlite;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use nulib\tests\TestCase;
|
|
||||||
|
|
||||||
class SqliteTest extends TestCase {
|
|
||||||
const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)";
|
|
||||||
const INSERT_JEPHTE = "insert into person(nom, prenom, age) values ('clain', 'jephte', 50)";
|
|
||||||
const INSERT_JEAN = "insert into person(nom, prenom, age) values ('payet', 'jean', 32)";
|
|
||||||
|
|
||||||
function testMigration() {
|
|
||||||
$sqlite = new Sqlite(":memory:", [
|
|
||||||
"migrate" => [
|
|
||||||
self::CREATE_PERSON,
|
|
||||||
self::INSERT_JEPHTE,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
self::assertSame("clain", $sqlite->get("select nom, age from person"));
|
|
||||||
self::assertSame([
|
|
||||||
"nom" => "clain",
|
|
||||||
"age" => 50,
|
|
||||||
], $sqlite->get("select nom, age from person", null, true));
|
|
||||||
|
|
||||||
$sqlite->exec(self::INSERT_JEAN);
|
|
||||||
self::assertSame("payet", $sqlite->get("select nom, age from person where nom = 'payet'"));
|
|
||||||
self::assertSame([
|
|
||||||
"nom" => "payet",
|
|
||||||
"age" => 32,
|
|
||||||
], $sqlite->get("select nom, age from person where nom = 'payet'", null, true));
|
|
||||||
|
|
||||||
self::assertSame([
|
|
||||||
["key" => "0", "value" => self::CREATE_PERSON, "done" => 1],
|
|
||||||
["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1],
|
|
||||||
], iterator_to_array($sqlite->all("select key, value, done from _migration")));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testException() {
|
|
||||||
$sqlite = new Sqlite(":memory:");
|
|
||||||
self::assertException(Exception::class, [$sqlite, "exec"], "prout");
|
|
||||||
self::assertException(SqliteException::class, [$sqlite, "exec"], ["prout"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function assertInserted(Sqlite $sqlite, array $row, array $query): void {
|
|
||||||
$sqlite->exec($query);
|
|
||||||
self::assertSame($row, $sqlite->one("select * from mapping where i = :i", [
|
|
||||||
"i" => $query["values"]["i"],
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
function testInsert() {
|
|
||||||
$sqlite = new Sqlite(":memory:", [
|
|
||||||
"migrate" => "create table mapping (i integer, s varchar)",
|
|
||||||
]);
|
|
||||||
$sqlite->exec(["insert into mapping", "values" => ["i" => 1, "s" => "un"]]);
|
|
||||||
$sqlite->exec(["insert mapping", "values" => ["i" => 2, "s" => "deux"]]);
|
|
||||||
$sqlite->exec(["insert into", "into" => "mapping", "values" => ["i" => 3, "s" => "trois"]]);
|
|
||||||
$sqlite->exec(["insert", "into" => "mapping", "values" => ["i" => 4, "s" => "quatre"]]);
|
|
||||||
$sqlite->exec(["insert into mapping(i)", "values" => ["i" => 5, "s" => "cinq"]]);
|
|
||||||
$sqlite->exec(["insert into (i)", "into" => "mapping", "values" => ["i" => 6, "s" => "six"]]);
|
|
||||||
$sqlite->exec(["insert into mapping(i) values ()", "values" => ["i" => 7, "s" => "sept"]]);
|
|
||||||
$sqlite->exec(["insert into mapping(i) values (8)", "values" => ["i" => 42, "s" => "whatever"]]);
|
|
||||||
$sqlite->exec(["insert into mapping(i, s) values (9, 'neuf')", "values" => ["i" => 43, "s" => "garbage"]]);
|
|
||||||
$sqlite->exec(["insert into mapping", "cols" => ["i"], "values" => ["i" => 10, "s" => "dix"]]);
|
|
||||||
|
|
||||||
self::assertSame([
|
|
||||||
["i" => 1, "s" => "un"],
|
|
||||||
["i" => 2, "s" => "deux"],
|
|
||||||
["i" => 3, "s" => "trois"],
|
|
||||||
["i" => 4, "s" => "quatre"],
|
|
||||||
["i" => 5, "s" => null/*"cinq"*/],
|
|
||||||
["i" => 6, "s" => null/*"six"*/],
|
|
||||||
["i" => 7, "s" => null/*"sept"*/],
|
|
||||||
["i" => 8, "s" => null/*"huit"*/],
|
|
||||||
["i" => 9, "s" => "neuf"],
|
|
||||||
["i" => 10, "s" => null/*"dix"*/],
|
|
||||||
], iterator_to_array($sqlite->all("select * from mapping")));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSelect() {
|
|
||||||
$sqlite = new Sqlite(":memory:", [
|
|
||||||
"migrate" => "create table user (name varchar, amount integer)",
|
|
||||||
]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "jclain1", "amount" => 1]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "jclain2", "amount" => 2]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "jclain5", "amount" => 5]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "fclain7", "amount" => 7]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "fclain9", "amount" => 9]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "fclain10", "amount" => 10]]);
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one("select * from user where name = 'jclain1'"));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one(["select * from user where name = 'jclain1'"]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one(["select from user where name = 'jclain1'"]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one(["select from user where", "where" => ["name = 'jclain1'"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one(["select from user", "where" => ["name = 'jclain1'"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one(["select", "from" => "user", "where" => ["name = 'jclain1'"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
"amount" => 1,
|
|
||||||
], $sqlite->one(["select", "from" => "user", "where" => ["name" => "jclain1"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
], $sqlite->one(["select name", "from" => "user", "where" => ["name" => "jclain1"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
], $sqlite->one(["select", "cols" => "name", "from" => "user", "where" => ["name" => "jclain1"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"name" => "jclain1",
|
|
||||||
], $sqlite->one(["select", "cols" => ["name"], "from" => "user", "where" => ["name" => "jclain1"]]));
|
|
||||||
self::assertSame([
|
|
||||||
"plouf" => "jclain1",
|
|
||||||
], $sqlite->one(["select", "cols" => ["plouf" => "name"], "from" => "user", "where" => ["name" => "jclain1"]]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSelectGroupBy() {
|
|
||||||
$sqlite = new Sqlite(":memory:", [
|
|
||||||
"migrate" => "create table user (name varchar, amount integer)",
|
|
||||||
]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "jclain1", "amount" => 1]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "jclain2", "amount" => 1]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "jclain5", "amount" => 2]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "fclain7", "amount" => 2]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "fclain9", "amount" => 2]]);
|
|
||||||
$sqlite->exec(["insert into user", "values" => ["name" => "fclain10", "amount" => 3]]);
|
|
||||||
|
|
||||||
self::assertSame([
|
|
||||||
["count" => 2],
|
|
||||||
], iterator_to_array($sqlite->all(["select count(name) as count from user", "group by" => ["amount"], "having" => ["count(name) = 2"]])));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\db\sqlite;
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class _queryTest extends TestCase {
|
|
||||||
function testParseConds(): void {
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(null, $sql, $params);
|
|
||||||
self::assertNull($sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds([], $sql, $params);
|
|
||||||
self::assertNull($sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col" => null], $sql, $params);
|
|
||||||
self::assertSame(["col is null"], $sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col = 'value'"], $sql, $params);
|
|
||||||
self::assertSame(["col = 'value'"], $sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds([["col = 'value'"]], $sql, $params);
|
|
||||||
self::assertSame(["col = 'value'"], $sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["int" => 42, "string" => "value"], $sql, $params);
|
|
||||||
self::assertSame(["(int = :int and string = :string)"], $sql);
|
|
||||||
self::assertSame(["int" => 42, "string" => "value"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["or", "int" => 42, "string" => "value"], $sql, $params);
|
|
||||||
self::assertSame(["(int = :int or string = :string)"], $sql);
|
|
||||||
self::assertSame(["int" => 42, "string" => "value"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
|
|
||||||
self::assertSame(["((int = :int and string = :string) and (int = :int2 and string = :string2))"], $sql);
|
|
||||||
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["int" => ["is null"], "string" => ["<>", "value"]], $sql, $params);
|
|
||||||
self::assertSame(["(int is null and string <> :string)"], $sql);
|
|
||||||
self::assertSame(["string" => "value"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col" => ["between", "lower", "upper"]], $sql, $params);
|
|
||||||
self::assertSame(["col between :col and :col2"], $sql);
|
|
||||||
self::assertSame(["col" => "lower", "col2" => "upper"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col" => ["in", "one"]], $sql, $params);
|
|
||||||
self::assertSame(["col in (:col)"], $sql);
|
|
||||||
self::assertSame(["col" => "one"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col" => ["in", ["one", "two"]]], $sql, $params);
|
|
||||||
self::assertSame(["col in (:col, :col2)"], $sql);
|
|
||||||
self::assertSame(["col" => "one", "col2" => "two"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col" => ["=", ["one", "two"]]], $sql, $params);
|
|
||||||
self::assertSame(["col = :col and col = :col2"], $sql);
|
|
||||||
self::assertSame(["col" => "one", "col2" => "two"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["or", "col" => ["=", ["one", "two"]]], $sql, $params);
|
|
||||||
self::assertSame(["col = :col or col = :col2"], $sql);
|
|
||||||
self::assertSame(["col" => "one", "col2" => "two"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["col" => ["<>", ["one", "two"]]], $sql, $params);
|
|
||||||
self::assertSame(["col <> :col and col <> :col2"], $sql);
|
|
||||||
self::assertSame(["col" => "one", "col2" => "two"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_conds(["or", "col" => ["<>", ["one", "two"]]], $sql, $params);
|
|
||||||
self::assertSame(["col <> :col or col <> :col2"], $sql);
|
|
||||||
self::assertSame(["col" => "one", "col2" => "two"], $params);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testParseValues(): void {
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values(null, $sql, $params);
|
|
||||||
self::assertNull($sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values([], $sql, $params);
|
|
||||||
self::assertNull($sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values(["col = 'value'"], $sql, $params);
|
|
||||||
self::assertSame(["col = 'value'"], $sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values([["col = 'value'"]], $sql, $params);
|
|
||||||
self::assertSame(["col = 'value'"], $sql);
|
|
||||||
self::assertNull($params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values(["int" => 42, "string" => "value"], $sql, $params);
|
|
||||||
self::assertSame(["int = :int", "string = :string"], $sql);
|
|
||||||
self::assertSame(["int" => 42, "string" => "value"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values(["int" => 42, "string" => "value"], $sql, $params);
|
|
||||||
self::assertSame(["int = :int", "string = :string"], $sql);
|
|
||||||
self::assertSame(["int" => 42, "string" => "value"], $params);
|
|
||||||
|
|
||||||
$sql = $params = null;
|
|
||||||
_query_base::parse_set_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
|
|
||||||
self::assertSame(["int = :int", "string = :string", "int = :int2", "string = :string2"], $sql);
|
|
||||||
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace nur\sery\file\base;
|
|
||||||
|
|
||||||
use nur\sery\file\FileReader;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class FileReaderTest extends TestCase {
|
|
||||||
function testIgnoreBom() {
|
|
||||||
# la lecture avec et sans BOM doit être identique
|
|
||||||
## sans BOM
|
|
||||||
$reader = new FileReader(__DIR__ . '/impl/sans_bom.txt');
|
|
||||||
self::assertSame("0123456789", $reader->fread(10));
|
|
||||||
self::assertSame(10, $reader->ftell());
|
|
||||||
$reader->seek(30);
|
|
||||||
self::assertSame("abcdefghij", $reader->fread(10));
|
|
||||||
self::assertSame(40, $reader->ftell());
|
|
||||||
$reader->seek(10);
|
|
||||||
self::assertSame("ABCDEFGHIJ", $reader->fread(10));
|
|
||||||
self::assertSame(20, $reader->ftell());
|
|
||||||
$reader->seek(40);
|
|
||||||
self::assertSame("0123456789\n", $reader->getContents());
|
|
||||||
$reader->close();
|
|
||||||
## avec BOM
|
|
||||||
$reader = new FileReader(__DIR__ . '/impl/avec_bom.txt');
|
|
||||||
self::assertSame("0123456789", $reader->fread(10));
|
|
||||||
self::assertSame(10, $reader->ftell());
|
|
||||||
$reader->seek(30);
|
|
||||||
self::assertSame("abcdefghij", $reader->fread(10));
|
|
||||||
self::assertSame(40, $reader->ftell());
|
|
||||||
$reader->seek(10);
|
|
||||||
self::assertSame("ABCDEFGHIJ", $reader->fread(10));
|
|
||||||
self::assertSame(20, $reader->ftell());
|
|
||||||
$reader->seek(40);
|
|
||||||
self::assertSame("0123456789\n", $reader->getContents());
|
|
||||||
$reader->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testCsvAutoParams() {
|
|
||||||
$reader = new FileReader(__DIR__ . '/impl/msexcel.csv');
|
|
||||||
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
|
|
||||||
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
|
|
||||||
self::assertNull($reader->fgetcsv());
|
|
||||||
$reader->close();
|
|
||||||
|
|
||||||
$reader = new FileReader(__DIR__ . '/impl/ooffice.csv');
|
|
||||||
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
|
|
||||||
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
|
|
||||||
self::assertNull($reader->fgetcsv());
|
|
||||||
$reader->close();
|
|
||||||
|
|
||||||
$reader = new FileReader(__DIR__ . '/impl/weird.tsv');
|
|
||||||
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
|
|
||||||
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
|
|
||||||
self::assertNull($reader->fgetcsv());
|
|
||||||
$reader->close();
|
|
||||||
|
|
||||||
$reader = new FileReader(__DIR__ . '/impl/avec_bom.csv');
|
|
||||||
self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
|
|
||||||
self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
|
|
||||||
self::assertNull($reader->fgetcsv());
|
|
||||||
$reader->close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
nom,prenom,age
|
|
||||||
clain,jephte,50
|
|
|
@ -1 +0,0 @@
|
|||||||
0123456789ABCDEFGHIJ0123456789abcdefghij0123456789
|
|
@ -1,2 +0,0 @@
|
|||||||
nom;prenom;age
|
|
||||||
clain;jephte;50
|
|
|
@ -1,2 +0,0 @@
|
|||||||
nom,prenom,age
|
|
||||||
clain,jephte,50
|
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user